How do I integrate Salesforce with Google Maps? - google-maps

How do I integrate Salesforce with Google Maps? I'm just looking for information on how to...
Search for contacts in Salesforce
Plot those on a google map.

EDIT:
Thanks to tggagne's comment I've realized that people still see this answer. The code that was here is over 2.5 years old. If you want to see it - check the history of edits.
A lot has changed in the meantime, more mashup examples were created. Not the least of them being "SF Bus Radar" (github, youtube) app by Cory Cowgill (created on Dreamforce'11 I think).
Nonetheless - here's my updated example with server-side geocoding, new field of type Geolocation and usage of JSON parsers.
It tries to cache the geocoding results in the contact records. Bear in mind it might not be 'production-ready' (no Google Business API key = as all our requests come out from same pool of Salesforce IP servers there might be error messages). That's why I've left the client-side geocoding too.
You'll need to make 2 changes in your environment before checking it out:
Add "Remote Site Setting" that points to https://maps.googleapis.com to enable callouts from Apex
Add field "Location" in Setup -> Customize -> Contacts -> fields. Type should be "Geolocation". I've selected display as decimals and precision of 6 decimal places.
public with sharing class mapController {
public String searchText {get;set;}
public List<Contact> contacts{get; private set;}
public static final String GEOCODING_URI_BASE = 'https://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=';
// For purposes of this demo I'll geocode only couple of addresses server-side. Real code can use the commented out value.
public static final Integer MAX_CALLOUTS_FROM_APEX = 3; // Limits.getLimitCallouts()
public mapController(){
searchText = ApexPages.currentPage().getParameters().get('q');
}
public void find() {
if(searchText != null && searchText.length() > 1){
List<List<SObject>> results = [FIND :('*' + searchText + '*') IN ALL FIELDS RETURNING
Contact (Id, Name, Email, Account.Name,
MailingStreet, MailingCity, MailingPostalCode, MailingState, MailingCountry,
Location__Latitude__s, Location__Longitude__s)
];
contacts = (List<Contact>)results[0];
if(contacts.isEmpty()){
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'No matches for "' + searchText + '"'));
} else {
serverSideGeocode();
}
} else {
if(contacts != null) {
contacts.clear();
}
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'Please provide at least 2 characters for the search.'));
}
}
public void clearGeocodedData(){
for(Contact c : contacts){
c.Location__Latitude__s = c.Location__Longitude__s = null;
}
Database.update(contacts, false);
contacts.clear();
}
public String getContactsJson(){
return JSON.serialize(contacts);
}
public String getDebugContactsJson(){
return JSON.serializePretty(contacts);
}
private void serverSideGeocode(){
List<Contact> contactsToUpdate = new List<Contact>();
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setTimeout(10000);
for(Contact c : contacts){
if((c.Location__Latitude__s == null || c.Location__Longitude__s == null)){
String address = c.MailingStreet != null ? c.MailingStreet + ' ' : '' +
c.MailingCity != null ? c.MailingCity + ' ' : '' +
c.MailingState != null ? c.MailingState + ' ' : '' +
c.MailingPostalCode != null ? c.MailingPostalCode + ' ' : '' +
c.MailingCountry != null ? c.MailingCountry : '';
if(address != ''){
req.setEndpoint(GEOCODING_URI_BASE + EncodingUtil.urlEncode(address, 'UTF-8'));
try{
HttpResponse res = h.send(req);
GResponse gr = (GResponse) JSON.deserialize(res.getBody(), mapController.GResponse.class);
if(gr.status == 'OK'){
LatLng ll = gr.results[0].geometry.location;
c.Location__Latitude__s = ll.lat;
c.Location__Longitude__s = ll.lng;
contactsToUpdate.add(c);
} else {
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Geocoding of "' + address + '" failed:' + gr.status));
}
}catch(Exception e){
ApexPages.addMessages(e);
}
}
// Bail out if we've reached limit of callouts (not all contacts might have been processed).
if(Limits.getCallouts() == MAX_CALLOUTS_FROM_APEX) {
break;
}
}
}
if(!contactsToUpdate.isEmpty()) {
Database.update(contactsToUpdate, false); // some data in Developer editions is invalid (on purpose I think).
// If update fails because "j.davis#expressl&t.net" is not a valid Email, I want the rest to succeed
}
}
// Helper class - template into which results of lookup will be parsed. Some fields are skipped!
// Visit https://developers.google.com/maps/documentation/geocoding/#Results if you need to create full mapping.
public class GResponse{
public String status;
public GComponents[] results;
}
public class GComponents{
public String formatted_address;
public GGeometry geometry;
}
public class GGeometry {
public LatLng location;
}
public class LatLng{
public Double lat, lng;
}
}
<apex:page controller="mapController" tabStyle="Contact" action="{!find}" id="page">
<head>
<style>
div #map_canvas { height: 400px; }
</style>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
</head>
<apex:sectionHeader title="Hello StackOverflow!" subtitle="Contact full text search + Google Maps integration" />
<apex:pageMessages />
<apex:form id="form">
<apex:pageBlock id="searchBlock">
<apex:inputText value="{!searchText}" />
<apex:commandButton value="Search" action="{!find}"/>
<p>Examples: "USA", "Singapore", "Uni", "(336) 222-7000". If it works in the global search box, it will work here.</p>
</apex:pageBlock>
<apex:pageBlock title="Found {!contacts.size} Contact(s)..." rendered="{!NOT(ISNULL(contacts)) && contacts.size > 0}" id="resultsBlock">
<apex:pageBlockButtons location="top">
<apex:commandButton value="Clear cached locations" title="Click if you want to set 'null' as geolocation info for all these contacts" action="{!clearGeocodedData}" />
</apex:pageBlockButtons>
<apex:pageBlockTable value="{!contacts}" var="c" id="contacts">
<apex:column headerValue="{!$ObjectType.Contact.fields.Name.label}">
<apex:outputLink value="../{!c.Id}">{!c.Name}</apex:outputLink>
</apex:column>
<apex:column headerValue="Address">
{!c.MailingStreet} {!c.MailingCity} {!c.MailingCountry}
</apex:column>
<apex:column value="{!c.Account.Name}"/>
<apex:column headerValue="Location (retrieved from DB or geocoded server-side)">
{!c.Location__Latitude__s}, {!c.Location__Longitude__s}
</apex:column>
</apex:pageBlockTable>
<apex:pageBlockSection columns="1" id="mapSection">
<div id="map_canvas" />
</apex:pageBlockSection>
<apex:pageBlockSection title="Click to show/hide what was geocoded server-side and passed to JS for further manipulation" columns="1" id="debugSection">
<pre>{!debugContactsJson}</pre>
</apex:pageBlockSection>
<pre id="log"></pre>
</apex:pageBlock>
</apex:form>
<script type="text/javascript">
twistSection(document.getElementById('page:form:resultsBlock:debugSection').childNodes[0].childNodes[0]); // initially hide the debug section
var contacts = {!contactsJson}; // Array of contact data, some of them might have lat/long info, some we'll have to geocode client side
var coords = []; // Just the latitude/longitude for each contact
var requestCounter = 0;
var markers = []; // Red things we pin to the map.
var balloon = new google.maps.InfoWindow(); // Comic-like baloon that floats over markers.
function geocodeClientSide() {
for(var i = 0; i < contacts.length; i++) {
if(contacts[i].Location__Latitude__s != null && contacts[i].Location__Longitude__s != null) {
coords.push(new google.maps.LatLng(contacts[i].Location__Latitude__s, contacts[i].Location__Longitude__s));
} else {
++requestCounter;
var address = contacts[i].MailingStreet + ' ' + contacts[i].MailingCity + ' ' + contacts[i].MailingCountry;
var geocoder = new google.maps.Geocoder();
if (geocoder) {
geocoder.geocode({'address':address}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
coords.push(results[0].geometry.location);
} else {
var pTag = document.createElement("p");
pTag.innerHTML = status;
document.getElementById('log').appendChild(pTag);
}
if(--requestCounter == 0) {
drawMap();
}
});
}
}
}
// It could be the case that all was geocoded on server side (or simply retrieved from database).
// So if we're lucky - just proceed to drawing the map.
if(requestCounter == 0) {
drawMap();
}
}
function drawMap(){
var mapOptions = {
center: coords[0],
zoom: 3,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
for(var i = 0; i < coords.length; ++i){
var marker = new google.maps.Marker({map: map, position: coords[i], title:contacts[i].Name, zIndex:i});
google.maps.event.addListener(marker, 'click', function() {
var index = this.zIndex;
balloon.content = '<b>'+contacts[index].Name + '</b><br/>' + contacts[index].Account.Name + '<br/>' + contacts[index].Email;
balloon.open(map,this);
});
markers.push(marker);
}
}
geocodeClientSide();
</script>
</apex:page>

Another place to look is the force.com platform fundamentals book (or site if you don't have a developer account). They have a very good and detailed tutorial here showing how to integrate maps with Salesforce (they use Yahoo for the tutorial but it will work just as well with Google Maps).

Since Spring '15, we can also use apex:map with no extra Google API.
Also works when viewed in Lightning -- No personal experience specifically but that's what I read.
Example from Docs:
<apex:map width="600px" height="400px" mapType="roadmap" center="{!Account.BillingStreet}, {!Account.BillingCity}, {!Account.BillingState}">
<!-- Add a CUSTOM map marker for the account itself -->
<apex:mapMarker title="{! Account.Name }" position="{!Account.BillingStreet}, {!Account.BillingCity}, {!Account.BillingState}" icon="{! URLFOR($Resource.MapMarkers, 'moderntower.png') }"/>
<!-- Add STANDARD markers for the account's contacts -->
<apex:repeat value="{! Account.Contacts }" var="ct">
<apex:mapMarker title="{! ct.Name }" position="{! ct.MailingStreet }, {! ct.MailingCity }, {! ct.MailingState }"></apex:mapMarker>
</apex:repeat>
</apex:map>
In the example, {! Account.Contacts } is a list of Contacts which
is being iterated over. Each iteration, it's creating apex:mapMarker's to map all Contacts in a list. Though the OP is old, the "search results" could basically replace the {Account.Contacts} list being iterated over in example.
Documentation:
Docs that example was pulled from.
(I know this is old but was brought to top from an update so thought update not using API would be okay.)

Related

Getting past permissions for a file through the API/Apps Script

I'm trying to create a list of files stored in my Google Drive and also a list of their current and previous permissions. Specifically, I want to create a list of files in my Google Drive which at any point in the past have had the 'Anyone with a link can view/edit (etc)' permission set.
I have created a Google Apps Script to do this and I can iterate through all the files OK and I can get files which currently have that permission set, but I can't see a way to get the history of the file's permissions.
I have found and activated the revisions list API: https://developers.google.com/drive/api/v2/reference/revisions/list
This gets revisions but I can't see anywhere that it lists the sharing history of a revision.
Is what I'm attempting to do possible?
It's definitely possible using the Drive Activity API. You can use the Quickstart for Google Apps Script to view all the activity of an item (file or folder) or done by a User. In this case I modified the Quickstart to show the Permissions changes of a given Drive Id.
function listDriveActivity() {
var request = {
itemName: "items/1bFQvSJ8pMdss4jInrrg7bxdae3dKgu-tJqC1A2TktMs", //Id of the file
pageSize: 10};
var response = DriveActivity.Activity.query(request);
var activities = response.activities;
if (activities && activities.length > 0) {
Logger.log('Recent activity:');
for (var i = 0; i < activities.length; i++) {
var activity = activities[i];
var time = getTimeInfo(activity);
var action = getActionInfo(activity.primaryActionDetail);
var actors = activity.actors.map(getActorInfo);
var targets = activity.targets.map(getTargetInfo);
if (action == "permissionChange"){ //Only show permissionChange activity
Logger.log(
'%s: %s, %s, %s', time, truncated(actors), action,
truncated(targets));
}
}
} else {
Logger.log('No activity.');
}
}
/** Returns a string representation of the first elements in a list. */
function truncated(array, opt_limit) {
var limit = opt_limit || 2;
var contents = array.slice(0, limit).join(', ');
var more = array.length > limit ? ', ...' : '';
return '[' + contents + more + ']';
}
/** Returns the name of a set property in an object, or else "unknown". */
function getOneOf(object) {
for (var key in object) {
return key;
}
return 'unknown';
}
/** Returns a time associated with an activity. */
function getTimeInfo(activity) {
if ('timestamp' in activity) {
return activity.timestamp;
}
if ('timeRange' in activity) {
return activity.timeRange.endTime;
}
return 'unknown';
}
/** Returns the type of action. */
function getActionInfo(actionDetail) {
return getOneOf(actionDetail);
}
/** Returns user information, or the type of user if not a known user. */
function getUserInfo(user) {
if ('knownUser' in user) {
var knownUser = user.knownUser;
var isMe = knownUser.isCurrentUser || false;
return isMe ? 'people/me' : knownUser.personName;
}
return getOneOf(user);
}
/** Returns actor information, or the type of actor if not a user. */
function getActorInfo(actor) {
if ('user' in actor) {
return getUserInfo(actor.user)
}
return getOneOf(actor);
}
/** Returns the type of a target and an associated title. */
function getTargetInfo(target) {
if ('driveItem' in target) {
var title = target.driveItem.title || 'unknown';
return 'driveItem:"' + title + '"';
}
if ('drive' in target) {
var title = target.drive.title || 'unknown';
return 'drive:"' + title + '"';
}
if ('fileComment' in target) {
var parent = target.fileComment.parent || {};
var title = parent.title || 'unknown';
return 'fileComment:"' + title + '"';
}
return getOneOf(target) + ':unknown';
}
Remember to enable the Drive Activity API in Resources > Advanced Google Services
In my example this returns the logs:
You can also look deeper into the Permissions by using the permissionChange Parameters in the query.
If you have a business/enterprise/edu account the admin audit logs will tell you this for 6 months of data. Or it will at least tell you when a permission was changed from x to y.
Can't think of a method for personal.

How to extract city, country, latitude and longitude from google places result

I am using the following Google Places script to get the location of the user. I get the full address, but I need to parse the result to get city, country.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Google Places Autocomplete textbox using google maps api</title>
</head>
<body>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true&libraries=places"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.js"></script>
<script type="text/javascript">
google.maps.event.addDomListener(window, 'load', initialize);
function initialize() {
var autocomplete = new google.maps.places.Autocomplete(document.getElementById('txtAutocomplete'));
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
var location = "<b>Address</b>: " + place.formatted_address + "<br/>";
location += "<b>Latitude</b>: " + place.geometry.location.lat() + "<br/>";
location += "<b>Longitude</b>: " + place.geometry.location.lng();
document.getElementById('lblResult').innerHTML = location;
});
}
</script>
<span>Location:</span>
<input type="text" id="txtAutocomplete" style="width: 300px" placeholder="Enter your address" /><br /><br />
<label id="lblResult" />
</body>
</html>
I've tried the below script, but it does not work all the time as in some cases, address format is different.
var city = place.address_components[0] && place.address_components[0].short_name || '';
document.getElementById('lblResult').innerHTML = location;
console.log(city);
If you have a look at console.log(place); you'll see something like...
The returned object contains address_components. This is an array which is created from the available data so you can't guarantee what fields it will contain. More details about what is returned can be found in the description of the Google Places API Web Service.
You need to loop through the array and extract out the fields that you need. city may not exist but postal_town or locality might in which case you'll want to use those values instead.
Bear in mind that you may not get any value for city or country if that data is not available.
There is some sample code on the Google Developers Site which does most of what you need.
I like to use Lodash, because it provides defense against errors when a property is missing, or the passed-in value is not the expected type.
While I'm sure there's more elegant ways, I've developed this method for plucking certain pieces of information from Google Places information:
ES5 version Fiddle:
var address = place.address_components;
var city, state, zip;
address.forEach(function(component) {
var types = component.types;
if (types.indexOf('locality') > -1) {
city = component.long_name;
}
if (types.indexOf('administrative_area_level_1') > -1) {
state = component.short_name;
}
if (types.indexOf('postal_code') > -1) {
zip = component.long_name;
}
});
var lat = place.geometry.location.lat;
var lng = place.geometry.location.lng;
Utilizing Lodash:
var address = _.get(place, 'address_components');
var city, state, zip;
_.forEach(address, function (component) {
let types = _.get(component, 'types');
if (_.includes(types, 'locality')) {
city = _.get(component, 'long_name');
}
if (_.includes(types, 'administrative_area_level_1')) {
state = _.get(component, 'short_name');
}
if (_.includes(types, 'postal_code')) {
zip = _.get(component, 'long_name');
}
});
var lat = _.get(place, 'geometry.location.lat');
var lng = _.get(place, 'geometry.location.lng');

GMap Api geocoder.getLatLng (address) function , Doesn't work good when call it within loop

I'm trying this recursion function to find LatLng of 2088 diffrent addresses and replay me with only about 180 results . although all addresses are valid on google maps website .
function test(i)
{
if(i >= jsArray.length){return;}
var geocoder = new GClientGeocoder();
geocoder.getLatLng(jsArray[i], function (current) {
return function(point) {
if (!point) {
data.push("null");
//nulls.push(myindex);
} else {
data.push(point);
//alert("done");
}
test(i+1,jsArray);
}
}(i));
}
test(0);
i have developed this recursive function but it's need about 30 mins till get good results ,
function test2(i)
{
geocoder.getLocations(jsArray[i], function (current) {
return function(response) {
if (!response || response.Status.code != 200) {
//alert(address + " not found");
//test(i,jsArray);
// data.push("null");
//nulls.push(myindex);
test2(i);
} else {
var len = response.Placemark[0];
point2 = new GLatLng(
len.Point.coordinates[1],
len.Point.coordinates[0]
);
data[i] = point2;
}
}
}(i));
}
for(i =0 ; i<=jsArray.length; i++)
{
if(i==jsArray.length){
alert(data.length);
/// $("#maintable").show(100) ;
/// $("#loading").hide(100) ;
}else{
test2(i);
}
}
i still need expert one to help me :) :D
The geocoder is asynchronous (which makes using it in loops problematic) and subject to a quota and rate limits. It is not intended for displaying lots of known addresses on a map, it is intended for user entered data.
You really should geocode your points off line, save the resulting coordinates and use those coordinates to display markers on your map.
If you are using it in a loop, you shouldn't use the getLatLng method, you should use the getLocations method, which contains a status code that will let you know why it is failing (G_GEO_TOO_MANY_QUERIES
= 620, would mean you could throttle your requests and potentially get a useful result)
// jsArray is array of addresses . the length of this array is 2087 element, all addresses got from google maps .
function MYFunction(i)
{
geocoder.getLocations(jsArray[i], function (current) {
return function(response) {
if (!response || response.Status.code != 200) {
test2(i); // recursive calling
} else {
var len = response.Placemark[0];
point2 = new GLatLng(
len.Point.coordinates[1],
len.Point.coordinates[0]
);
data[i] = point2;
}
}
}(i));
} /// end of y Function
//// loop for each address and pass it to MyFunction function and start recursive function .
for(i =0 ; i<=jsArray.length; i++)
{
MYFunction(i);
}

Country name from latitude and longitude

This question may look familiar: I have the latitude and longitude of a place. I need to get the name of country. I know I have to use reverse geo coding for this. But my problem is that sometimes it returns the short form of the area or country (for example US for United State or CA for California). Is there any way that I can get the full name of the country? I can't perform a match operation by this short forms with my prestored country database.
I have already gone through this, this. But it's not much help for my problem.
The geocoder response usually returns several results which include street corners, intersections, counties, and other alternate representation names. I found that results[0] is the best description.
The trick is searching for "country" in the results. Then the long_name can be retrieved.
Click on the map
function getCountry(latLng) {
geocoder.geocode( {'latLng': latLng},
function(results, status) {
if(status == google.maps.GeocoderStatus.OK) {
if(results[0]) {
for(var i = 0; i < results[0].address_components.length; i++) {
if(results[0].address_components[i].types[0] == "country") {
alert(results[0].address_components[i].long_name);
}
}
}
else {
alert("No results");
}
}
else {
alert("Status: " + status);
}
}
);
}
The JSON array normally includes both long_name and short_name. You should be able to extract both...
Here is an JSON/XML parser that works for both google street maps and open street maps.
(the only problem is that it requires a JSON or XML object as the "reply" its tested on version 3 google and 0.6 open street maps and it works good)
NOTE: it returns an object location.lat or location.lon you can also have it return whatever other field you want.
JSON.parse(text) // where text is the reply from google or open street maps
XML.parse(text) // you can make your own to convert the reply to XML or use regex to parse it. If someone has a regex version to parse the text reply that may also be helpful.
// Parser(ajax reply object, google/open, json/xml);
// takes the reply from google maps or open street maps and creates an object with location[lat/lon]
function Parser(reply, provider, type) {
var location = {};
if(reply != null) {
if(provider == "google") { // Google Street Maps
switch(type) {
case "xml":
location["lat"] = reply.getElementsByTagName("lat")[0].textContent;
location["lon"] = reply.getElementsByTagName("lng")[0].textContent;
break;
default: // json
location["lat"] = reply.results[0].geometry.location.lat;
location["lon"] = reply.results[0].geometry.location.lng;
}
}
else { // Open Street Maps
switch(type) {
case "xml":
location["lat"] = reply.getElementsByTagName("place")[0].getAttribute("lat");
location["lon"] = reply.getElementsByTagName("place")[0].getAttribute("lon");
break;
default: // json
location["lat"] = reply[0].lat;
location["lon"] = reply[0].lon;
}
}
}
return location;
}
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
$.post("https://maps.googleapis.com/maps/api/geocode/json?latlng=" + position.coords.latitude + "," + position.coords.longitude + "&sensor=false", function (result) {
for (var i = 0; i < result['results'][0]['address_components'].length; i++) {
if (result['results'][0]['address_components'][i]['types'][0] == "country") {
alert(result['results'][0]['address_components'][i]['long_name']);
}
}
});
});
}
}
getLocation();

Google Map Integrated with Salesforce is Blank

I followed the example code line by line from:
How do I integrate Salesforce with Google Maps?
I am not receiving any errors but my Map is blank. The map displays the zoom and position toggle but nothing else.
Any ideas on why?
Here is my Controller Code and Page Code Below
public class mapController2 {
public String address {get;set;}
private List<Account> accounts;
public void find() {
// Normal, ugly query.
/* address = 'USA';
String addr = '%' + address + '%';
accounts = [SELECT Id, Name, BillingStreet, BillingCity, BillingCountry FROM Account
//WHERE Name LIKE :addr OR BillingStreet LIKE :addr OR BillingCity LIKE :addr OR BillingCountry LIKE :addr];
WHERE BillingCountry LIKE :addr];*/
// address = 'Austin';
String addr = '*' + address + '*';
// Or maybe a bit better full-text search query.
List<List<SObject>> searchList = [FIND :addr IN ALL FIELDS RETURNING
Account (Id, Name, BillingStreet, BillingCity, BillingState, BillingCountry)];
accounts = (List<Account>)searchList[0];
}
public List<Account> getAccounts() {
return accounts;
}
}
Page Code
<apex:page controller="mapController2" tabStyle="Account">
<head>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<style>
#map {
height:350px;
}
</style>
</head>
<apex:form >
<apex:pageBlock title="Search by Address">
<apex:inputText value="{!address}"/>
<apex:commandButton value="Search" action="{!find}"/>
<p>Examples: "USA", "Singapore", "Uni", "(336) 222-7000".
Any text data (free text, not picklists, checkboxes etc.) will do.
</p>
</apex:pageBlock>
<apex:pageBlock title="Matching Accounts" rendered="{!address != null}">
<apex:pageBlockTable value="{!accounts}" var="account" id="accountTable">
<apex:column >
<apex:facet name="header"><b>Name</b></apex:facet>
<apex:outputLink value="../{!account.Id}">{!account.Name}</apex:outputLink>
</apex:column>
<apex:column >
<apex:facet name="header"><b>Address</b></apex:facet>
{!account.BillingStreet}, {!account.BillingCity}, {!account.BillingCountry}
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
<apex:pageBlock title="Map" rendered="{!address != null}">
<div id="log"></div>
<p>Tip: hover mouse over marker to see the Account name. Click to show the baloon.</p>
<div id="map">Placeholder - map will be created here.</div>
<script type="text/javascript">
// First we need to extract Account data (name and address) from HTML into JavaScript variables.
var names = new Array();
var addresses = new Array();
var htmlTable = document.getElementById('j_id0:j_id2:j_id7:accountTable').getElementsByTagName("tbody")[0].getElementsByTagName("tr");
for (var i = 0; i < htmlTable.length; ++i) {
names.push(htmlTable[i].getElementsByTagName("td")[0]);
// We need to sanitize addresses a bit (remove newlines and extra spaces).
var address = htmlTable[i].getElementsByTagName("td")[1].innerHTML;
addresses.push(address.replace(/\n/g, "").replace(/^\s+/,"").replace(/\s+$/,""));
}
var coordinates = new Array(); // Array of latitude/longitude coordinates.
var markers = new Array(); // Red things we pin to the map.
var baloons = new Array(); // Comic-like baloons that can float over markers.
var counter = 0;
var mapOptions = {
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP,
navigationControl: true,
navigationControlOptions: {
style: google.maps.NavigationControlStyle.ZOOM_PAN
}
};
var map = new google.maps.Map(document.getElementById("map"), mapOptions);
if(addresses.length > 0) {
geocodeOneAddress();
}
function geocodeOneAddress(){
var geocoder = new google.maps.Geocoder();
geocoder.geocode({address: addresses[counter]}, processGeocodingResults);
}
function processGeocodingResults(results, status) {
++counter;
if (status == google.maps.GeocoderStatus.OK) {
coordinates.push(results[0].geometry.location);
} else {
logError(addresses[counter] + " could not be found, reason: " + status);
}
if(counter == addresses.length) {
finalizeDrawingMap();
} else {
geocodeOneAddress();
}
}
function finalizeDrawingMap() {
// Compute min/max latitude and longitude so we know where is the best place to center map & zoom.
var minLat = coordinates[0].b;
var maxLat = coordinates[0].b;
var minLong = coordinates[0].c;
var maxLong = coordinates[0].c;
for(i=0;i < coordinates.length; i++){
markers.push(new google.maps.Marker({ position: coordinates[i], map: map, title: names[i].getElementsByTagName("a")[0].innerHTML, zIndex:i}));
baloons.push(new google.maps.InfoWindow({content: '<b>'+names[i].innerHTML + '</b><br/>' + addresses[i]}));
google.maps.event.addListener(markers[i], 'click', function() {
baloons[this.zIndex].open(map,this);
});
minLat = Math.min(minLat, coordinates[i].b);
maxLat = Math.max(maxLat, coordinates[i].b);
minLong = Math.min(minLong, coordinates[i].c);
maxLong = Math.max(maxLong, coordinates[i].c);
}
map.setCenter(new google.maps.LatLng(minLat + (maxLat-minLat) / 2, minLong + (maxLong-minLong) / 2));
// All that is left is to possibly change the zoom. Let us compute the size of our rectangle.
// This is just a rough indication calculation of size of rectangle that covers all addresses.
var size = (maxLat-minLat) * (maxLong-minLong);
var zoom = 13;
if(size > 7100) {
zoom = 2;
}
else if(size > 6000) {
zoom = 3;
}
else if(size > 550) {
zoom = 4;
}
else if(size > 20) {
zoom = 6;
}
else if(size > 0.12) {
zoom = 9;
}
map.setZoom(zoom);
}
function logError(msg) {
var pTag = document.createElement("p");
pTag.innerHTML = msg;
document.getElementById('log').appendChild(pTag);
}
</script>
</apex:pageBlock>
</apex:form>
</apex:page>
Ya even I was getting a blank map but try to do
https://c.na3.visual.force.com/apex/map?id=0015000000UsQZR
Make sure you enter a valid account id to use.
<apex:page standardController="Account">
<script src="http://maps.google.com/maps?file=api">
</script>
<script type="text/javascript">
var map = null;
var geocoder = null;
var address = "{!Account.BillingStreet}, {!Account.BillingPostalCode} {!Account.BillingCity}, {!Account.BillingState}, {!Account.BillingCountry}";
function initialize() {
if(GBrowserIsCompatible())
{
map = new GMap2(document.getElementById("MyMap"));
map.addControl(new GMapTypeControl());
map.addControl(new GLargeMapControl3D());
geocoder = new GClientGeocoder();
geocoder.getLatLng(
address,
function(point) {
if (!point) {
document.getElementById("MyMap").innerHTML = address + " not found";
} else {
map.setCenter(point, 13);
var marker = new GMarker(point);
map.addOverlay(marker);
marker.bindInfoWindowHtml("Account Name : <b><i> {!Account.Name} </i></b>
Address : "+address);
}
}
);
}
}
</script>
<div id="MyMap" style="width:100%;height:300px"></div>
<script>
initialize() ;
</script>
</apex:page>
Hope this helps
Thanks
tosha
And use the code given as:
src="http://maps.google.com/maps/api/js?sensor=false"
Use
https
not
http
It will definately be blank in firefox or chrome if using http and not https. Try https.