客户端 Web 应用中的 Earth Engine API

本指南介绍了如何构建网页,以显示在 Earth Engine 中实时计算的互动式地图结果。适用于掌握了 HTML、CSS 和 JavaScript 的初级或中级知识的人士。

以下是您将在本指南中创建的互动式地图的演示。该地图显示了在 Earth Engine 中计算出的科罗拉多大峡谷的地形坡度,以不同深浅的灰色表示。您可以平移和缩放地图,以计算和显示其他区域的结果。请注意,您必须登录才能使用演示。

此处介绍的方法通常用于创建单页应用 (SPA)。在 SPA 中,整个应用可在 Web 浏览器中使用,而无需从服务器重新加载网页。因此,这些应用不需要开发和托管自定义服务器端组件。

由于应用的代码将在用户的网络浏览器中运行(也称为客户端执行),因此开发者不应将服务账号私钥等敏感凭据直接嵌入到应用中。相反,应用的用户必须使用自己的账号进行身份验证,这些账号已注册 Earth Engine 访问权限。

在本指南中,您将了解如何完成以下操作:

  1. 显示一个按钮,允许用户使用其 Earth Engine 账号登录。
  2. 在 Earth Engine 中定义基本分析。
  3. 嵌入互动式地图,以使用 Maps JavaScript API 显示结果。

前提条件

设置 Cloud 项目

  1. 在开始之前,请按照设置已启用 Earth Engine 的 Cloud 项目中的说明操作。记下“设置 OAuth 2.0”部分中获得的客户端 ID。由于您的应用将允许用户使用自己的 Google 账号登录,因此您可以跳过“创建和注册服务账号”部分。
  2. 为您的项目启用 Maps JavaScript API

获取地图 API 密钥

如需了解如何获取 API 密钥,以便在 Web 应用中使用 Maps JavaScript API,请参阅 Maps JavaScript API 文档中的获取 API 密钥

强烈建议您还应按照限制 API 密钥部分中的说明操作,以确保只能使用您的 API 密钥发出已授权的请求。

打造您的应用

第 1 步:创建 HTML 网页

首先,按如下方式定义一个基本的 HTML 网页:

<!DOCTYPE html>
<html>
  <head>
    <style>
      /* Set the size of the div element that contains the map. */
      #map-container {
        height: 400px;
        width: 100%;
        background-color: #eee;
      }
    </style>
  </head>
  <body>
    <!-- The "Sign in with Google" button, initially hidden. -->
    <input
      id="g-sign-in"
      type="image"
      src="https://developers.google.com/identity/images/btn_google_signin_light_normal_web.png"
      onclick="onSignInButtonClick()"
      alt="Sign in with Google"
      hidden
    />

    <!-- Element where map will be added. -->
    <div id="map-container"></div>
    <script>
      // JavaScript code goes here.
    </script>
  </body>
</html>

此基本 HTML 会执行以下几项操作:

  • 使用 CSS 样式定义初始化时显示的地图的大小和背景颜色。
  • 定义一个“使用 Google 账号登录”按钮,该按钮在点击时会调用函数 onSignInButtonClick()。此函数将在后续部分中以 JavaScript 定义。
  • 定义一个空元素,该元素将在地图初始化后包含地图。
  • 添加一个空的 <script> 块,用于存放下面定义的 JavaScript 代码。

第 2 步:在 JavaScript 中定义行为

在后续步骤中,JavaScript 代码可以直接放置在 <script> 标记内。

定义回调以设置 API 和地图

定义一个函数,用于在用户登录后执行工作。只有在用户通过身份验证后,才能初始化和调用 Earth Engine API。稍后会详细介绍。

此示例会初始化 Earth Engine 和 Maps API,创建一个按需计算地形坡度的图块源,并将该图块源作为 Maps JavaScript API 显示的叠加层添加到地图中:

// Initializes Maps JavaScript API and Earth Engine API, instructing the map
// to pull tiles from Earth Engine and to overlay them on the map.
function setUpMap() {
  // Hide the sign-in button.
  document.getElementById("g-sign-in").setAttribute("hidden", "true");

  // Initialize the Earth Engine API. Must be called once before using the API.
  ee.initialize(null, null, null, null, null, 'my-project');

  // Get a reference to the placeholder DOM element to contain the map.
  const mapContainerEl = document.getElementById("map-container");

  // Create an interactive map inside the placeholder DOM element.
  const embeddedMap = new google.maps.Map(mapContainerEl, {
    // Pan and zoom initial map viewport to Grand Canyon.
    center: {lng: -112.8598, lat: 36.2841},
    zoom: 9,
  });

  // Obtain reference to digital elevation model and apply algorithm to
  // calculate slope.
  const srtm = ee.Image("CGIAR/SRTM90_V4");
  const slope = ee.Terrain.slope(srtm);

  // Create a new tile source to fetch visible tiles on demand and display them
  // on the map.
  const mapId = slope.getMap({min: 0, max: 60});
  const tileSource = new ee.layers.EarthEngineTileSource(mapId);
  const overlay = new ee.layers.ImageOverlay(tileSource);
  embeddedMap.overlayMapTypes.push(overlay);
}

为登录按钮点击定义处理程序

接下来,添加一个函数,用于在用户点击登录按钮时显示登录弹出式窗口。如果成功,则会调用 setUp() 方法。

// Handles clicks on the sign-in button.
function onSignInButtonClick() {
  // Display popup allowing the user to sign in with their Google account and to
  // grant appropriate permissions to the app.
  ee.data.authenticateViaPopup(setUpMap);
}

此回调通过其 onclick 属性与上述 HTML 中的按钮相关联。请注意,在此示例中,登录按钮在页面加载时最初处于隐藏状态。在下一部分中,我们将检查用户是否已登录,然后再显示该按钮。

定义主要入口点

现在,定义在网页加载时首先执行的顶级代码。它使用 Earth Engine 的内置身份验证辅助函数 ee.data.authenticateViaPopup() 来检查用户是否已登录。如果用户已登录,该函数会请求相应的权限,并在成功时调用 setUp() 来初始化 Earth Engine 和 Maps API。

如果用户未登录,则会显示登录按钮。然后,用户可以点击该按钮来触发 onSignInButtonClick(),进而显示弹出式窗口并调用 setUp() 来启动操作。

// If the user is signed in, display a popup requesting permissions needed to
// run the app, otherwise show the sign-in button.
ee.data.authenticateViaOauth(
  // The OAuth Client ID defined above.
  CLIENT_ID,
  // Callback invoked immediately when user is already signed in.
  setUpMap,
  // Show authentication errors in a popup.
  alert,
  // Request permission to only read and compute Earth Engine data on behalf of
  // user.
  /* extraScopes = */ ['https://www.googleapis.com/auth/earthengine.readonly'],
  // Show sign-in button if reusing existing credentials fails.
  () => document.getElementById("g-sign-in").removeAttribute("hidden"),
  // Don't require ability to write and access Cloud Platform on behalf of the
  // user.
  /* opt_suppressDefaultScopes = */ true
);

亲自尝试

本指南中提供的完整解决方案如下所示。示例代码的右上角有三个按钮。点击最左侧的按钮可在 JSFiddle 中打开该示例。

YOUR_API_KEYYOUR_CLIENT_ID 替换为在前提条件中获取的地图 API 密钥和 OAuth 客户端 ID(您可以点击以下代码中的这些占位符,让系统自动插入相应的值)。另请将 'my-project' 替换为您的 Google Cloud 项目 ID。

<!-- Load Maps JavaScript API. For production apps, append you own ?key=YOUR_API_KEY. -->

<script src="https://maps.googleapis.com/maps/api/js?key="></script>
<script src="https://ajax.googleapis.com/ajax/libs/earthengine/0.1.365/earthengine-api.min.js"></script>
<!-- The "Sign in with Google" button, initially hidden. -->
<input
  id="g-sign-in"
  type="image"
  src="https://developers.google.com/identity/images/btn_google_signin_light_normal_web.png"
  onclick="onSignInButtonClick()"
  alt="Sign in with Google"
  hidden
/>

<!-- Element where map will be added. -->
<div id="map-container"></div>
/* Set the size of the div element that contains the map. */
  #map-container {
    height: 400px;
    width: 100%;
    background-color: #eee;
  }
// The OAuth Client ID from the Google Developers Console.
// REMINDER: Be sure to add a valid ID here!
const CLIENT_ID = "";

// Initializes Maps JavaScript API and Earth Engine API, instructing the map
// to pull tiles from Earth Engine and to overlay them on the map.
function setUpMap() {
  // Hide the sign-in button.
  document.getElementById("g-sign-in").setAttribute("hidden", "true");

  // Initialize the Earth Engine API. Must be called once before using the API.
  ee.initialize(null, null, null, null, null, 'my-project');

  // Get a reference to the placeholder DOM element to contain the map.
  const mapContainerEl = document.getElementById("map-container");

  // Create an interactive map inside the placeholder DOM element.
  const embeddedMap = new google.maps.Map(mapContainerEl, {
    // Pan and zoom initial map viewport to Grand Canyon.
    center: {lng: -112.8598, lat: 36.2841},
    zoom: 9,
  });

  // Obtain reference to digital elevation model and apply algorithm to
  // calculate slope.
  const srtm = ee.Image("CGIAR/SRTM90_V4");
  const slope = ee.Terrain.slope(srtm);

  // Create a new tile source to fetch visible tiles on demand and display them
  // on the map.
  const mapId = slope.getMap({min: 0, max: 60});
  const tileSource = new ee.layers.EarthEngineTileSource(mapId);
  const overlay = new ee.layers.ImageOverlay(tileSource);
  embeddedMap.overlayMapTypes.push(overlay);
}

// Handles clicks on the sign-in button.
function onSignInButtonClick() {
  // Display popup allowing the user to sign in with their Google account and to
  // grant appropriate permissions to the app.
  ee.data.authenticateViaPopup(setUpMap);
}

// If the user is signed in, display a popup requesting permissions needed to
// run the app, otherwise show the sign-in button.
ee.data.authenticateViaOauth(
  // The OAuth Client ID defined above.
  CLIENT_ID,
  // Callback invoked immediately when user is already signed in.
  setUpMap,
  // Show authentication errors in a popup.
  alert,
  // Request permission to only read and compute Earth Engine data on behalf of
  // user.
  /* extraScopes = */ ['https://www.googleapis.com/auth/earthengine.readonly'],
  // Show sign-in button if reusing existing credentials fails.
  () => document.getElementById("g-sign-in").removeAttribute("hidden"),
  // Don't require ability to write and access Cloud Platform on behalf of the
  // user.
  /* opt_suppressDefaultScopes = */ true
);
<!DOCTYPE html>
<html>
  <head>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/earthengine/0.1.365/earthengine-api.min.js"></script>
    <style>
      /* Set the size of the div element that contains the map. */
      #map-container {
        height: 400px;
        width: 100%;
        background-color: #eee;
      }
    </style>
  </head>
  <body>
    <!-- The "Sign in with Google" button, initially hidden. -->
    <input
      id="g-sign-in"
      type="image"
      src="https://developers.google.com/identity/images/btn_google_signin_light_normal_web.png"
      onclick="onSignInButtonClick()"
      alt="Sign in with Google"
      hidden
    />

    <!-- Element where map will be added. -->
    <div id="map-container"></div>
    <script>
      // The OAuth Client ID from the Google Developers Console.
      const CLIENT_ID = "YOUR_CLIENT_ID";
      
      // Initializes Maps JavaScript API and Earth Engine API, instructing the map
      // to pull tiles from Earth Engine and to overlay them on the map.
      function setUpMap() {
        // Hide the sign-in button.
        document.getElementById("g-sign-in").setAttribute("hidden", "true");
      
        // Initialize the Earth Engine API. Must be called once before using the API.
        ee.initialize(null, null, null, null, null, 'my-project');
      
        // Get a reference to the placeholder DOM element to contain the map.
        const mapContainerEl = document.getElementById("map-container");
      
        // Create an interactive map inside the placeholder DOM element.
        const embeddedMap = new google.maps.Map(mapContainerEl, {
          // Pan and zoom initial map viewport to Grand Canyon.
          center: {lng: -112.8598, lat: 36.2841},
          zoom: 9,
        });
      
        // Obtain reference to digital elevation model and apply algorithm to
        // calculate slope.
        const srtm = ee.Image("CGIAR/SRTM90_V4");
        const slope = ee.Terrain.slope(srtm);
      
        // Create a new tile source to fetch visible tiles on demand and display them
        // on the map.
        const mapId = slope.getMap({min: 0, max: 60});
        const tileSource = new ee.layers.EarthEngineTileSource(mapId);
        const overlay = new ee.layers.ImageOverlay(tileSource);
        embeddedMap.overlayMapTypes.push(overlay);
      }
      
      // Handles clicks on the sign-in button.
      function onSignInButtonClick() {
        // Display popup allowing the user to sign in with their Google account and to
        // grant appropriate permissions to the app.
        ee.data.authenticateViaPopup(setUpMap);
      }
      
      // If the user is signed in, display a popup requesting permissions needed to
      // run the app, otherwise show the sign-in button.
      ee.data.authenticateViaOauth(
        // The OAuth Client ID defined above.
        CLIENT_ID,
        // Callback invoked immediately when user is already signed in.
        setUpMap,
        // Show authentication errors in a popup.
        alert,
        // Request permission to only read and compute Earth Engine data on behalf of
        // user.
        /* extraScopes = */ ['https://www.googleapis.com/auth/earthengine.readonly'],
        // Show sign-in button if reusing existing credentials fails.
        () => document.getElementById("g-sign-in").removeAttribute("hidden"),
        // Don't require ability to write and access Cloud Platform on behalf of the
        // user.
        /* opt_suppressDefaultScopes = */ true
      );
    </script>
  </body>
</html>