2010-06-25 13 views

Antwort

11

EDIT:

Dank tggagne ‚s Bemerkung, die ich realisiert habe, dass die Menschen immer noch diese Antwort sehen. Der Code, der hier war, ist über 2,5 Jahre alt. Wenn Sie es sehen möchten, überprüfen Sie den Verlauf der Änderungen.

Eine Menge hat sich in der Zwischenzeit geändert, mehr Mashup-Beispiele wurden erstellt. Nicht die geringste von ihnen ist "SF Bus Radar" (github, youtube) App von Cory Cowgill (erstellt auf Dreamforce'11 denke ich).

Dennoch - hier ist mein aktualisierte Beispiel mit serverseitigen Geokodierung, neuem Feld vom Typ Geolokalisierung und die Verwendung von JSON-Parser.

Es versucht, die Geocoding-Ergebnisse in den Kontaktdatensätzen zwischenzuspeichern. Bedenken Sie, dass es möglicherweise nicht produktionsbereit ist (kein Google Business API-Schlüssel = alle unsere Anforderungen stammen aus demselben Pool von Salesforce IP-Servern there might be error messages). Deshalb habe ich auch die clientseitige Geokodierung verlassen.


Sie werden zwei Änderungen in Ihrer Umgebung vornehmen müssen, bevor sie auszuchecken:

  1. "Remote Site Setting" hinzufügen, die https://maps.googleapis.com Punkte Legenden von Apex ermöglichen
  2. Fügen Sie das Feld "Ort" in Setup -> Anpassen -> Kontakte -> Felder hinzu. Typ sollte "Geolocation" sein. Ich habe Anzeige als Dezimalstellen und Genauigkeit von 6 Dezimalstellen ausgewählt.

    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

Gibt es einen Grund, warum die Schnittstelle vollständig in implementiert Javascript und nicht Apex? – tggagne

+2

Bitte beachten Sie, dass die Verwendung der Google Maps-API in Salesforce den Kauf einer entsprechenden API-Lizenz von Google erfordert. Wenn Sie dies ohne die entsprechende Lizenzierung verwenden, kann dies zu einem Ausfall des Dienstes führen. –

1

Ein weiterer Ort zu suchen ist die force.com Plattform Fundamental book (oder site wenn Sie kein Entwickler-Konto haben). Sie haben ein sehr gutes und ausführliches Tutorial here, das zeigt, wie man Karten mit Salesforce integriert (sie benutzen Yahoo für das Tutorial, aber es funktioniert genauso gut mit Google Maps).

1

Seit dem Frühjahr '15, können wir auch apex:map ohne zusätzlichen Google-API verwenden. Funktioniert auch, wenn in Lightning - Keine persönliche Erfahrung speziell, aber das ist, was ich gelesen habe.

Beispiel von Text & Tabellen:

<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 dem Beispiel ist {! Account.Contacts } eine Liste von Kontakten, die iteriert wird.Bei jeder Iteration wird apex:mapMarker erstellt, um alle Kontakte in einer Liste zuzuordnen. Obwohl das OP alt ist, könnten die "Suchergebnisse" die {Account.Contacts} Liste ersetzen, die im Beispiel iteriert wird.

Dokumentation: Docs that example was pulled from.

(Ich weiß, das ist alt, aber gebracht wurde nach oben aus einem Update so dachte Update wäre okay, nicht API.)