2010-06-25 23 views

Respuesta

11

EDITAR:

Gracias al comentario tggagne 's me he dado cuenta que la gente todavía ver esta respuesta. El código que estaba aquí tiene más de 2,5 años. Si quieres verlo, consulta el historial de ediciones.

Un lote ha cambiado mientras tanto, se han creado más ejemplos de mashup. No menos importante de ellos es "SF Bus Radar" (github, youtube) aplicación de Cory Cowgill (creada en Dreamforce'11, creo).

No obstante, aquí está mi ejemplo actualizado con geocodificación del lado del servidor, nuevo campo de tipo Geolocalización y uso de analizadores JSON.

Trata de almacenar en caché los resultados de geocodificación en los registros de contacto. Tenga en cuenta que podría no estar "listo para la producción" (ninguna clave de Google Business API = ya que todas nuestras solicitudes provienen del mismo grupo de servidores de Salesforce IP there might be error messages). Es por eso que también dejé la geocodificación del lado del cliente.


Tendrá que hacer 2 cambios en su entorno antes de echarle un vistazo:

  1. Añadir "Remote Site Setting" que apunta a https://maps.googleapis.com para permitir llamadas de Apex
  2. Agregue el campo "Ubicación" en Configuración -> Personalizar -> Contactos -> campos. Tipo debe ser "Geolocalización". He seleccionado mostrar como decimales y precisión de 6 lugares decimales.

    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

¿Hay alguna razón la interfaz está completamente implementada en Javascript y no Apex? – tggagne

+2

Tenga en cuenta que el uso de la API de Google Maps desde Salesforce requiere la compra de licencias API correspondiente de Google. Usar esto sin la licencia apropiada en su lugar puede ocasionar la pérdida de servicio en cualquier momento. –

1

Otro lugar para buscar es el force.com fundamentos plataforma book (o site si usted no tiene una cuenta de desarrollador). Tienen un tutorial muy bueno y detallado here que muestra cómo integrar mapas con Salesforce (utilizan Yahoo para el tutorial pero funcionará igual de bien con Google Maps).

1

Desde Spring '15, también podemos usar apex:map sin Google API adicional. También funciona cuando se ve en Lightning: sin experiencia personal específica, pero eso es lo que leo.

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

En el ejemplo, {! Account.Contacts } es una lista de contactos que está siendo repiten a lo largo.En cada iteración, está creando apex:mapMarker para asignar todos los contactos en una lista. Aunque el PO es viejo, los "Resultados de la búsqueda" básicamente podría sustituir a la lista {Account.Contacts} que se repiten a lo largo en el ejemplo.

Documentación: Docs that example was pulled from.

(Sé que esto es viejo, pero trajeron al principio de una actualización por lo pensó actualización no utilizando la API estaría bien.)