使用 Visualforce 创建地图

地图传达的信息比单纯的信息更清晰 位置数据。Visualforce 映射 组件使创建使用第三方地图服务的地图变得简单。Visualforce 地图是交互式的, 基于 JavaScript 的地图,包括缩放、平移和基于 Salesforce 或其他数据的标记。创建独立版 地图页面、可插入页面布局的地图,甚至是 Salesforce 的移动地图 应用程序。

Visualforce 提供了一组相关的映射 组件。该组件定义地图 画布,包括大小、类型、中心点和初始缩放级别。子组件定义要放置的标记 在地图上按地址或地理位置(纬度和经度)。您可以使用该组件添加可自定义的 单击或点击标记时显示的信息面板。<apex:map><apex:mapMarker><apex:mapInfoWindow>

注意

Visualforce 映射组件不是 在 Developer Edition 组织中可用。

您在 Visualforce 标记中定义的地图会生成 JavaScript 代码 以呈现到页面上。此 JavaScript 连接到地图服务,并通过以下方式构建地图 获取地图图块并放置标记。如果要映射的项目没有纬度和 经度,Visualforce 地图可以进行地理编码 他们的地址。地图渲染后,用户可以通过平移和 缩放,就像他们习惯于其他地图站点一样。效果就好像你自己写的一样 自定义 JavaScript 与第三方映射服务进行交互,但实际上不需要 写出来。您可以在 Visualforce 中定义地图并免费获取地图 JavaScript。

重要

Visualforce 映射 组件将 JavaScript 添加到页面,并使用第三方 JavaScript 代码绘制地图。

  • Visualforce 添加的 JavaScript 使用 行业标准最佳实践,避免与在同一 JavaScript 上执行的其他 JavaScript 发生冲突 页。如果您自己的 JavaScript 没有使用最佳实践,它可能会与 映射代码。
  • 需要地理编码的地址 – 即不包含纬度和 经度 – 发送到第三方服务进行地理编码。这些地址未关联 ,除了您在 Visualforce 标记中提供的数据外,不会发送其他数据。但是,如果您的 组织要求严格控制在 Salesforce 外部共享的数据,不要使用地理编码 Visualforce地图的功能。
  • 创建基本地图 没有标记的基本地图
    只需要一个组件。此组件定义地图的基本画布,包括其尺寸、位置和初始缩放级别。<apex:map>
  • 向地图添加位置标记 您可以使用该组件向地图
    添加标记以表示特定位置。您可以包括指针悬停在标记上时显示的文本。<apex:mapMarker>
  • 使用自定义标记图标
    Visualforce 地图标记图标功能强大,但很普通。要区分标记并向地图添加细节或样式,请使用自定义地图标记图标。
  • 将信息窗口添加到标记
    信息窗口允许您在地图上显示额外的详细信息。当用户单击或点击标记时,将显示信息窗口。
  • 在 Apex 中构建地图数据的示例 在 Apex
    中构建位置数据以执行自定义查询、搜索附近位置、筛选或转换结果,或者在无法使用 Visualforce 标准控制器返回的结果时执行。

创建基本地图

没有标记的基本地图只需要一个组件。此组件定义地图的基本画布,包括其 尺寸、位置和初始缩放级别。

<apex:map>该属性定义围绕该点的点 地图居中。您可以在以下几个方面提供值 格式。

centercenter

  • 一个表示地址的字符串。例如,“1 Market Street, San Francisco, CA”。这 对地址进行地理编码以确定其纬度和经度。
  • 一个字符串,表示具有指定位置的 JSON对象和属性 坐标。例如,“{latitude: 37.794, longitude: -122.395}”。latitudelongitude
  • 类型为 的 Apex 映射对象 with 和 键指定位置坐标。Map<String, Double>latitudelongitude

如果没有子标记,则该属性是必需的。

<apex:map><apex:mapMarker>center这张简单的街道地图显示了 Salesforce 旧金山周围的社区 总部。

<apex:page >

    <h1>Salesforce in San Francisco</h1>
  
    <!-- Display the address on a map -->
    <apex:map width="600px" height="400px" mapType="roadmap" zoomLevel="16"
        center="One Market Street, San Francisco, CA">
    </apex:map>
    
</apex:page>

这 代码生成以下映射。

请注意此示例中的以下内容。

  • 映射的地址没有标记。该组件本身不显示地图标记,即使对于中心点也是如此。要显示最多 100 个标记,添加子组件。<apex:map><apex:mapMarker>
  • 地图的位置值以 街道地址,而不是地理位置。地图服务查找纬度和经度 地址。此过程称为地理编码。您最多可以将 10 个地理编码地址作为属性或作为随组件添加的标记包含在地图中。centercenter<apex:mapMarker>
  • 值是“路线图”,一条标准街道 地图。其他选项是“卫星”和“混合”。mapType

向地图添加位置标记

您可以使用该组件向地图添加标记以表示特定位置。您可以包含以下文本 当指针悬停在标记上时显示。

<apex:mapMarker>

若要在地图上放置标记,请添加一个组件作为关联 .您可以使用属性指定标记的位置。(可选)使用该属性在指针悬停时显示文本 标记。<apex:mapMarker><apex:map>positiontitle您最多可以加到

100地图上的标记。 使用迭代组件添加 集合或列表中的多个标记。

<apex:repeat>

注意

Visualforce 地图可以是 资源密集型,可能导致移动浏览器和 Salesforce 内存问题 应用程序。具有许多标记的地图或用作自定义标记的大图像可以进一步增加 内存消耗。如果您计划在使用的页面中部署 Visualforce 地图 在移动设备环境中,请务必彻底测试这些网页。该属性定义地图上的点 放置标记。您可以提供值 有几种格式。

positionposition

  • 一个表示地址的字符串。例如,“1 Market Street, San Francisco, CA”。这 对地址进行地理编码以确定其纬度和经度。
  • 一个字符串,表示具有指定位置的 JSON对象和属性 坐标。例如,“{latitude: 37.794, longitude: -122.395}”。latitudelongitude
  • 类型为 的 Apex 映射对象 with 和 键指定位置坐标。Map<String, Double>latitudelongitude

注意

您最多可以拥有10每个地图的地理编码地址查找。对组件属性和组件属性的查找都计入此限制。自 显示更多标记,提供 不需要地理编码。超过地理编码限制的位置将被跳过。center<apex:map>position<apex:mapMarker>position下面是一个页面,其中显示了 帐户,以帐户的 地址。

<apex:page standardController="Account">

  <!-- This page must be accessed with an Account Id in the URL. For example: 
       https://<salesforceInstance>/apex/NearbyContacts?id=001D000000JRBet -->
  
  <apex:pageBlock >
    <apex:pageBlockSection title="Contacts For {! Account.Name }">
    
     <apex:dataList value="{! Account.Contacts }" var="contact">
       <apex:outputText value="{! contact.Name }" />
     </apex:dataList> 
    
  <apex:map width="600px" height="400px" mapType="roadmap"
    center="{!Account.BillingStreet},{!Account.BillingCity},{!Account.BillingState}">

    <apex:repeat value="{! Account.Contacts }" var="contact">
    <apex:mapMarker title="{! contact.Name }"
       position="{!contact.MailingStreet},{!contact.MailingCity},{!contact.MailingState}"
    />
    </apex:repeat>

  </apex:map>

    </apex:pageBlockSection>
  </apex:pageBlock>

</apex:page>

这 代码生成以下映射。

请注意此示例中的以下内容。

  • 和属性作为 Visualforce 表达式传递,该表达式 连接地址元素以提供可进行地理编码的地址字符串。centerposition
  • 由于此页面对地址使用地理编码,因此仅显示前 9 个地址 接触。属性使用一个地理编码查找作为 允许 10 个。(在图中,该帐户只有三个联系人。center<apex:map>

使用自定义标记图标

Visualforce 地图标记图标 功能齐全,但朴素无比。要区分标记并向地图添加细节或样式,请使用 自定义地图标记图标。要自定义标记的图标,请将属性设置为绝对或完全限定的 URL 要使用的图形。您可以引用 Web 上的任何图像,例如,如果您的图形是 分布在 CDN 中。您还可以使用存储在静态资源中的图形。如果您使用图像 从静态资源中,使用 URLFOR() 函数获取图像 URL。为 例:

icon

<apex:mapMarker title="{! Account.Name }" 
    position="{!Account.BillingStreet},{!Account.BillingCity},{!Account.BillingState}"
    icon="{! URLFOR($Resource.MapMarkers, 'moderntower.png') }" />

使用常见的图形格式,例如 PNG、GIF 或 JPEG格式。首选标记大小为 32 × 32 像素。其他尺寸是缩放的,但不会 始终产生理想的结果。

注意

Visualforce 地图可以是 资源密集型,可能导致移动浏览器和 Salesforce 内存问题 应用程序。具有许多标记的地图或用作自定义标记的大图像可以进一步增加 内存消耗。如果您计划在使用的页面中部署 Visualforce 地图 在移动设备环境中,请务必彻底测试这些网页。此完整页面演示了如何使用自定义标记来指示帐户的位置,以及 帐户的标准标记 接触。

<apex:page standardController="Account">

  <!-- This page must be accessed with an Account Id in the URL. For example: 
       https://<salesforceInstance>/apex/AccountContacts?id=001D000000JRBet -->
  
  <apex:pageBlock >
    <apex:pageBlockSection title="Contacts For {! Account.Name }">
    
      <apex:dataList value="{! Account.Contacts }" var="contact">
        <apex:outputText value="{! contact.Name }" />
      </apex:dataList> 
      
      <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>
    
    </apex:pageBlockSection>
  </apex:pageBlock>
   
</apex:page>

这 代码生成以下映射。

要对迭代中添加的标记使用不同的图标,例如 ,请使用与迭代相关的表达式 变量来定义 URL。一种简单的方法是在 记录。另一种方法是在自定义公式字段中提供图标名称。<apex:repeat>这是上一个块,带有 假定联系人对象具有名为“ContactType__c”的自定义字段的变体,并且 每种触点类型都有一个相应的名称 图标。

<apex:repeat>

<!-- Add CUSTOM 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 }"
          icon="{! URLFOR($Resource.MapMarkers, ct.ContactType__c + '.png') }">
        </apex:mapMarker>
    </apex:repeat>

如果 您使用字段来提供图标 URL 的关键部分,确保它始终提供 可用值。例如,通过将其设为必填字段,或确保公式字段 提供合理的默认值。

将信息窗口添加到标记

信息窗口允许您在地图上显示额外的详细信息。当用户 单击或点击标记。

地图标记属性允许您显示 当用户将鼠标悬停在标记上时,少量信息。要显示更多信息或 可以更好地控制其格式,使用信息窗口代替属性或添加属性。titletitle

例如,您可以显示联系人地址的完整详细信息,格式为最佳 显示。您可以添加可点击的电话链接,甚至可以显示对象的个人资料照片 有一个。

要将信息窗口添加到地图标记,请将组件添加为 关联的 .组件的主体显示在 用户单击或点击标记时的信息窗口,可以是 Visualforce 标记、HTML 和 CSS,或者 纯文本。<apex:mapInfoWindow><apex:mapMarker><apex:mapInfoWindow>此完整页面使用 Visualforce 标记 信息内容 窗。

<apex:page standardController="Account">

  <!-- This page must be accessed with an Account Id in the URL. For example: 
       https://<salesforceInstance>/apex/AccountContactsCustomMarker?id=001D000000JRBet -->
  
  <apex:pageBlock >
    <apex:pageBlockSection title="Contacts For {! Account.Name }">
    
      <apex:dataList value="{! Account.Contacts }" var="contact">
        <apex:outputText value="{! contact.Name }" />
      </apex:dataList> 
      
      <apex:map width="600px" height="400px" mapType="roadmap"
  center="{!Account.BillingStreet},{!Account.BillingCity},{!Account.BillingState}">

      <!-- Add markers for account contacts -->
      <apex:repeat value="{! Account.Contacts }" var="ct">
        <apex:mapMarker title="{! ct.Name }" 
          position="{! ct.MailingStreet },{! ct.MailingCity },{! ct.MailingState }">

          <!-- Add info window with contact details -->
          <apex:mapInfoWindow >
            <apex:outputPanel layout="block" style="font-weight: bold;">
              <apex:outputText>{! ct.Name }</apex:outputText>
            </apex:outputPanel>

            <apex:outputPanel layout="block">
              <apex:outputText>{! ct.MailingStreet }</apex:outputText>
            </apex:outputPanel>               

            <apex:outputPanel layout="block">
              <apex:outputText>{! ct.MailingCity }, {! ct.MailingState }</apex:outputText>
            </apex:outputPanel>               

            <apex:outputPanel layout="block">
              <apex:outputLink value="{! 'tel://' + ct.Phone }">
                  <apex:outputText>{! ct.Phone }</apex:outputText>
              </apex:outputLink>
            </apex:outputPanel>               
          </apex:mapInfoWindow>

        </apex:mapMarker>
      </apex:repeat>

      </apex:map>
    
    </apex:pageBlockSection>
  </apex:pageBlock>
   
</apex:page>

这 代码生成以下映射。

默认情况下,一次只显示一个信息窗口。当您单击另一个标记时,第一个标记 “信息”窗口将关闭,而“新建信息”窗口将打开。要同时显示多个信息窗口, 设置为 在包含组件上。

showOnlyActiveInfoWindowfalse<apex:map>

注意

仔细考虑显示多个的效果 信息窗口,因为它可以创建杂乱的地图。

在 Apex 中构建地图数据的示例

在 Apex 中构建您的位置数据以执行自定义查询,搜索附近 位置、过滤或转换结果,或者当您无法使用 Visualforce 标准返回的结果时 控制器。

Apex 代码使您可以完全控制返回并用于 地图和标记。您还可以使用 Apex 返回来自 Salesforce 外部的结果。此页面最多显示 10 个离用户最近的仓库 位置。

<apex:page controller="FindNearbyController" docType="html-5.0" >

    <!-- JavaScript to get the user's current location, and pre-fill
         the currentPosition form field. -->
    <script type="text/javascript">
        // Get location, fill in search field
    	function setUserLocation() {
            if (navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(function(loc){
                    var latlon = loc.coords.latitude + "," + loc.coords.longitude;
                    var el = document.querySelector("input.currentPosition");
                    el.value = latlon;
                });
            }
        }
        // Only set the user location once the page is ready
    	var readyStateCheckInterval = setInterval(function() {
    		if (document.readyState === "interactive") {
        		clearInterval(readyStateCheckInterval);
        		setUserLocation();
    		}
		}, 10);
    </script>
    
    <apex:pageBlock >
        <!-- Form field to send currentPosition in request. You can make it
             an <apex:inputHidden> field to hide it. -->
        <apex:pageBlockSection >
            <apex:form >
                <apex:outputLabel for="currentPosition">Find Nearby</apex:outputLabel> 
                <apex:input size="30" 
                     html-placeholder="Attempting to obtain your position..."
                     id="currentPosition" styleClass="currentPosition" 
                     value="{!currentPosition}" />
                <apex:commandButton action="{!findNearby}" value="Go!"/>
            </apex:form>
        </apex:pageBlockSection>
        
        <!-- Map of the results -->
        <apex:pageBlockSection rendered="{!resultsAvailable}" title="Locations">
            <apex:map width="600px" height="400px">
                <apex:repeat value="{!locations}" var="pos">
                    <apex:mapMarker position="{!pos}"/>
                </apex:repeat>
            </apex:map>
        </apex:pageBlockSection>

    </apex:pageBlock>

</apex:page>

这 代码生成以下映射。

此页面有三个重要部分。

  • 开头的 JavaScript 块说明了如何访问 浏览器的内置功能,可以询问用户的当前位置。此代码 更新可见的表单字段。但是,您可以轻松使用隐藏的表单字段 而是避免显示原始纬度和经度及其不太可能的级别 的精度。
  • 第一个包含一个简短的表单,用于在 POSTBACK 中提交用户的位置 请求。出于说明目的,它是可见的,需要单击,但这是 不需要。<apex:pageBlockSection>
  • 在第二种情况下,地图本身很简单,只需要 五行代码。所有的复杂性都在表达式中,它访问 Apex 中的属性 控制器。<apex:pageBlockSection>{!locations}请注意属性的用法,该属性采用表达式的值。这 expression 是另一个 Apex 属性,将它与该属性一起使用会隐藏 map 部分 当位置无法放置在地图上时。rendered{!resultsAvailable}rendered

这是支持以前的 Apex 控制器 页。

public with sharing class FindNearbyController {

    public List<Map<String,Double>> locations { get; private set; }

    public String currentPosition { 
        get {
            if (String.isBlank(currentPosition)) {
                currentPosition = '37.77493,-122.419416'; // San Francisco
            }
            return currentPosition;
        }
        set; 
    }
    
    public Boolean resultsAvailable {
        get {
            if(locations == Null) {
                return false;
            }
            return true;
        }
    }
    
    public PageReference findNearby() {
        String lat, lon;

        // FRAGILE: You'll want a better lat/long parsing routine
        // Format: "<latitude>,<longitude>" (must have comma, but only one comma)
        List<String> latlon = currentPosition.split(',');
        lat = latlon[0].trim();
        lon = latlon[1].trim();

        // SOQL query to get the nearest warehouses
        String queryString =
           'SELECT Id, Name, Location__longitude__s, Location__latitude__s ' +
           'FROM Warehouse__c ' +
           'WHERE DISTANCE(Location__c, GEOLOCATION('+lat+','+lon+'), \'mi\') < 20 ' +
           'ORDER BY DISTANCE(Location__c, GEOLOCATION('+lat+','+lon+'), \'mi\') ' +
           'LIMIT 10';

        // Run the query
        List <Warehouse__c> warehouses = database.Query(queryString);
        
        if(0 < warehouses.size()) {
            // Convert to locations that can be mapped
            locations = new List<Map<String,Double>>();
            for (Warehouse__c wh : warehouses) {
                locations.add(
                    new Map<String,Double>{
                        'latitude' => wh.Location__latitude__s, 
                        'longitude' => wh.Location__longitude__s
                    }
                );
            }
        }
        else {
            System.debug('No results. Query: ' + queryString);
        }
                
        return null;
    }
}

花几分钟时间了解有关此控制器的更多信息,以及它如何与 Visualforce 页面配合使用。

  • 该属性是元素的列表。此列表以可直接使用的格式保存位置数据 按组件。locationsMap<String,Double><apex:mapMarker>
  • 该属性捕获 从页面表单提交的位置信息。此属性 此外,如果表单提交为空,则有效的默认值为 提供。(更健壮的实现将对表单进行更多错误检查 输入。currentPosition
  • 该属性,在 前面对 Visualforce 标记的描述。resultsAvailable
  • 操作方法被调用 当 Go! 被按下。这 方法完成所有工作,执行自定义 SOQL 查询并处理 结果到属性中 格式。findNearby<apex:commandButton>locations

如果要使用 的属性来提供额外的 信息(例如,仓库名称),您有多种选择。如果你的 方法返回 sObjects,您可以在 Visualforce 标记中引用相应的字段。如果你是 直接创建新对象,就像我们在这里一样,你可以创建一个内部类 将位置地图对象与标题字符串组合在一起。然后,返回 内部类对象到页面。title<apex:mapMarker>