2010-06-25 106 views

回答

11

編輯

由於tggagne「的評論,我意識到,人們仍然可以看到這個答案。這裏的代碼已經超過2.5年了。如果你想看到它 - 檢查編輯的歷史。

很多在此期間發生了變化,創建了更多mashup示例。並不是他們中的至少一個是「科幻巴士雷達」(github,youtube)的應用程序科裏·考吉爾(在Dreamforce'11上創建的,我認爲)。

儘管如此,這裏是我更新的服務器端地理編碼示例,地理定位類型和JSON解析器使用的新字段。

它試圖將地理編碼結果緩存在聯繫人記錄中。請記住,它可能不是「生產就緒」(沒有Google Business API密鑰=因爲我們的所有請求都來自同一個Salesforce IP服務器池there might be error messages)。這就是爲什麼我也離開了客戶端地理編碼。


你需要檢查出來之前,讓您的環境中2點的變化:

  1. 添加 「Remote Site Setting」 指向https://maps.googleapis.com從頂點
  2. 使標註
  3. 在設置 - >自定義 - >聯繫人 - >字段中添加字段「位置」。類型應該是「地理定位」。我選擇顯示爲小數點,精度爲6位小數。

    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 "[email protected]&amp;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: <a href="/apex/{!$CurrentPage.Name}?q=USA">"USA"</a>, "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> 
+1

是否有一個原因的接口是否完全實施Javascript而不是Apex? – tggagne 2012-11-02 16:43:53

+2

請注意,在Salesforce中使用Google Maps API需要向Google購買適當的API許可。如果沒有適當的授權,在任何時候都可能導致服務丟失。 – 2013-11-08 02:38:44

1

另一個地方看是force.com平臺基本面book(或site,如果你沒有一個開發者賬戶)。他們有一個非常好的詳細教程here,展示瞭如何將地圖與Salesforce集成(他們使用Yahoo作爲教程,但它與Google地圖一樣好)。

1

自15年春季以來,我們也可以使用apex:map而不需要額外的Google API。 在閃電中查看時也有效 - 專門沒有個人經驗,但這就是我閱讀的內容。從文檔

實施例:

<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> 

在該示例中,{! Account.Contacts }是 被遍歷聯繫人的列表。每次迭代時,它都會創建apex:mapMarker來映射列表中的所有聯繫人。雖然OP是舊的,但「搜索結果」基本上可以取代正在迭代的例子中的{Account.Contacts}列表。

文檔: Docs that example was pulled from.

(我知道這是舊的,但被帶到頂部從更新所以認爲不使用API​​的更新會好起來的。)