Syntax Highlighter JS

Tuesday, December 11, 2012

A follow-up on google maps

UPDATE: As this example no longer works, Please see the updated version which can be found here.

I previously posted a Geocoder example for Google maps. Since that was posted, Salesforce has added Geolocation fields to their system.  I highly recommend that you use this new field type instead of the text field type in my original post.  This is so you can query by distance as seen in the example query from Salesforce.

This class is great for coding existing data and will eventually code new data coming into the system.  However, if you want a more real time coder (i.e. based off a trigger), you can see an example of that here.  Please be sure to read my comments at the bottom of that page for some improvements you might make if you use that class alone or in combination with my scheduled class.

If you are going to use scheduled classes (such as my Geocoder) in your system, then I highly recommend that you read this very interesting scheduled apex solution by Dan Appleman.

Once you have your data geocoded, you will most likely want to create your own Goolemaps mashup.  Here is a great GoogleMaps API tutorial to get you started.

You will want to look at the source to his example page to get you started with the JavaScript required to have a mash-up that paints some static points on the map.  Once you have a static example created, then you can explore how to integrate this with visual force to paint a dynamic list on the screen.

You will need to go ahead and create your apex controller for the page. Inside of that controller populate a list of objects from SOQL.  If want to do this based on distance, be sure to look back up at the example query in the first paragraph for an example.

For this example, I am going to query for 10 random accounts.  I am sure that your final code will be much more complex but I am focusing this post on the JavaScript and Visualforce required to make this work.

Please note that I am querying the Name and ID field in addtion to the BillingLong__c and BillingLat__c fields from the Geocoder example.  This is important because any data you want to present will need to be included in the query.  Here is the example controller:
public with sharing class TestController { public List<Account> AccountsList {get;set;} public TestController() { AccountsList = [SELECT ID, Name, BillingLat__c, BillingLong__c FROM Account LIMIT 10]; } // end constructor } // end class

Please note that you cannot use the standard Salesforce css with this example. Also, this example is using V3 of Google maps API which doesn't require a key. Here is the example page: <apex:page sidebar="false" showHeader="false" cache="false" controller="Test2Controller"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> <title>Google Maps Example</title> <script src="http://maps.google.com/maps?file=api&amp;v=3&amp;sensor=false" type="text/javascript"></script> <script type="text/javascript"> // map and geocoder vars var map = null; var geocoder = null; var BaseMarkerOptions = null; // This function should be called onload of the body of the page function initialize() { if (GBrowserIsCompatible()) { var polys = []; var labels = []; // Display the map, with some controls and set the initial location map = new GMap2(document.getElementById("map")); map.addControl(new GLargeMapControl()); map.addControl(new GMapTypeControl()); // You will need to adjust this location to be the center coordinates and zoom level that you want // You can set this using visual force if needed map.setCenter(new GLatLng(32.5206608,-86.80249),5); geocoder = new GClientGeocoder(); // use our default marker options SetMarkerOptions(); // use visualforce to pull in the data from the SOQL query and use javascript to paint the points on the screen // the funciton call is: showAddress(html, Lat, Long) // HTML is the code to be displayed in pop-up when the point/marker is clicked. // Notice that JSENCODE is used to prevent javascript errors <apex:repeat value="{!AccountsList}" var="Account"> showAddress("{!JSENCODE(Account.Name)} <br /> <a href='../{!Account.id}' target='_blank'>Details</a>", "{!Account.BillingLat__c}", "{!Account.BillingLong__c}"); </apex:repeat> // enable the mouse wheel for zooming. Alot of mash-ups don't enable this for some reason. map.enableScrollWheelZoom(); send(null); } // display a warning if the browser was not compatible else { alert("Sorry, the Google Maps API is not compatible with this browser"); } // GBrowserIsCompatible() } // function initialize() // this function sets the icon and size to be used. It is using the defaults ATM but could be changed. function SetMarkerOptions() { var baseIcon = new GIcon(G_DEFAULT_ICON); baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png"; baseIcon.iconSize = new GSize(20, 34); baseIcon.shadowSize = new GSize(37, 34); baseIcon.iconAnchor = new GPoint(9, 34); baseIcon.infoWindowAnchor = new GPoint(9, 2); BaseMarkerOptions = { icon:baseIcon }; } // SetCustomMarkerOptions // this is the function that actually adds a marker on the map from the point (long/lat) data. function createMarker(point,html) { var marker = new GMarker(point); GEvent.addListener(marker, "click", function() { marker.openInfoWindowHtml(html); }); IncrementRenderedPoints(); return marker; } function showAddress(html, Lat, Long) { var myLatlng = new GLatLng(parseFloat(Lat), parseFloat(Long)); if (myLatlng != null) { var marker = new GMarker(myLatlng, BaseMarkerOptions); GEvent.addListener(marker, "click", function() { marker.openInfoWindowHtml(html); }); map.addOverlay(marker); } // check for null myLatlng } // end show address </script> </head> <body onload="initialize()" onunload="GUnload()"> <div id="map" style="width: 1024px; height: 768px"></div> <noscript><b>JavaScript must be enabled in order for you to use Google Maps.</b> However, it seems JavaScript is either disabled or not supported by your browser. To view Google Maps, enable JavaScript by changing your browser options, and then try again. </noscript> </body> </html> </apex:page>
And here is what the example looks like:

Monday, December 3, 2012

Visual Force component to generate SOQL where clause


 This is a screen shot of the the "Create new view" page in sales force (standard on any tab).



Having this as a Visual Force component could be used for all kinds of handy things.  In this post, I demonstrate this component and have it return a where clause for SOQL.   It dynamically lists the accessible and filterable fields on the object passed.  Operators are automatically set based on the field type from the schema.

Looking back up at that screen shot one has to wonder, why isn't the list sorted alphabetically?

I think that a possible reason is that the platform lacks a direct way to sort a selection options list.

With permission, I have used a class from Mohammad Usman to sort this control alphabetically.  I have added a test method in my code but his post was found here.

This is an early but working edition.  I hope to eventually fully mirror the native functionality but in the mean time, please be aware that this version has some limitations:
  • It does not have advanced criteria. Right now multiple rows / clauses are and only.
    • Updated on 1/7/2013 to include advanced filtering.
  • Excludes and Includes are not yet supported as operators.
  • DateTime fields (not date fields) must be entered in the SOQL format.
  • No date picker for Date fields. 
    • Updated on 3/25/2013 to include the JQuery UI date picker widget and to reformat the date behind the scenes for SOQL use.
  • Picklist values must already be known as I have yet to add code to list them in a pop-up window like salesforce does.
    • Updated on 1/22/2013 to include a basic look-up window for picklist and name fields.
Since controllers can't directly modify primitives, a wrapper class named SOQLQueryFilterResult is used to leverage the fact that they can modify objects.  This is how the resulting data will be returned.

Here is a sample apex controller:
public with sharing class testControllerExt { public SOQLQueryFilterResult FilterResult {get; set;} public SObject Object2Filter {get; set;} public testControllerExt () { FilterResult = new SOQLQueryFilterResult(); Account MyAccount = new Account(); Object2Filter = MyAccount; } // testControllerExt constructor } // main class
And here is a sample of how the component would be called with that controller:

The full code can be found here.