‫Earth Engine API באפליקציות אינטרנט בצד הלקוח

במדריך הזה נסביר איך ליצור דף אינטרנט עם מפה אינטראקטיבית שמציגה תוצאות שחושבו בזמן אמת ב-Earth Engine. המדריך מתאים לאנשים עם ידע בסיסי או בינוני ב-HTML, ב-CSS וב-JavaScript.

בהמשך מוצגת הדגמה של המפה האינטראקטיבית שתיצרו במדריך הזה. במפה מוצג שיפוע פני השטח של הגרנד קניון, שחושב ב-Earth Engine ומיוצג על ידי גוונים שונים של אפור. אפשר להזיז את המפה ולהתקרב לתצוגה כדי לחשב ולהציג תוצאות של אזורים נוספים. שימו לב: צריך להתחבר כדי לראות את ההדגמה.

הגישה שמתוארת כאן משמשת בדרך כלל כשיוצרים אפליקציות של דף יחיד (SPA). באפליקציית SPA, אפשר להשתמש באפליקציה כולה בדפדפן אינטרנט בלי שהדף ייטען מחדש מהשרת. לכן, באפליקציות האלה לא נדרש פיתוח ואירוח של רכיב מותאם אישית בצד השרת.

מכיוון שהקוד של האפליקציה יפעל בדפדפן האינטרנט של המשתמש (הידוע גם כביצוע בצד הלקוח), מפתחים לא צריכים להטמיע פרטי כניסה רגישים כמו מפתחות פרטיים של חשבונות שירות ישירות באפליקציה. במקום זאת, משתמשי האפליקציה צריכים לבצע אימות באמצעות החשבונות שלהם שנרשמו לגישה ל-Earth Engine.

במדריך הזה תלמדו איך:

  1. הצגת לחצן שמאפשר למשתמשים להיכנס באמצעות חשבון Earth Engine שלהם.
  2. הגדרת ניתוח בסיסי ב-Earth Engine.
  3. הטמעת מפה אינטראקטיבית להצגת תוצאות באמצעות Maps JavaScript API.

דרישות מוקדמות

הגדרת הפרויקט ב-Cloud

  1. לפני שמתחילים, צריך לפעול לפי ההוראות שבמאמר הגדרת פרויקט Cloud עם Earth Engine. כדאי לרשום את מזהה הלקוח שמתקבל בקטע 'הגדרת OAuth 2.0'. מכיוון שהאפליקציה תאפשר למשתמשים להיכנס באמצעות חשבון Google שלהם, אפשר לדלג על הקטע 'יצירה ורישום של חשבון שירות'.
  2. מפעילים את Maps JavaScript API בפרויקט.

קבלת מפתח Maps API

במאמר קבלת מפתח API במאמרי העזרה של ה-Maps JavaScript API מוסבר איך מקבלים מפתח API שמאפשר להשתמש ב-Maps JavaScript 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>.

הגדרת קריאה חוזרת (callback) כדי להגדיר ממשקי API ולמפות

מגדירים פונקציה שתבצע פעולה אחרי שהמשתמש נכנס לחשבון. אפשר לאתחל את Earth Engine API ולהפעיל אותו רק אחרי שהמשתמש מאומת. בקרוב נשתף מידע נוסף בנושא.

בדוגמה הזו מתבצעת אתחול של ממשקי ה-API של Earth Engine ושל מפות Google, נוצר מקור של משבצות שבו מחושב שיפוע פני השטח לפי דרישה, ומקור המשבצות מתווסף למפה כשכבת-על שמוצגת על ידי 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);
}

הגדרת handler לקליקים על לחצן הכניסה

לאחר מכן, מוסיפים פונקציה להצגת חלון קופץ לכניסה כשלוחצים על לחצן הכניסה. אם הפעולה בוצעה ללא שגיאות, מתבצעת קריאה ל-method‏ 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);
}

הקריאה החוזרת הזו משויכת ללחצן בקוד ה-HTML שלמעלה באמצעות המאפיין onclick. שימו לב שבדוגמה הזו, לחצן הכניסה מוסתר בהתחלה כשהדף נטען. בקטע הבא נבדוק אם המשתמש מחובר לפני שנציג את הלחצן.

הגדרת נקודת כניסה ראשית

עכשיו צריך להגדיר את הקוד ברמה העליונה שיופעל ראשון כשהדף נטען. היא משתמשת בפונקציית העזר המובנית לאימות של Earth Engine‏ ee.data.authenticateViaPopup() כדי לבדוק אם המשתמש כבר מחובר. אם המשתמש מחובר לחשבון, הפונקציה מבקשת את ההרשאות המתאימות, ואם היא מצליחה, היא קוראת ל-setUp() כדי לאתחל את ממשקי ה-API של Earth Engine ומפות Google.

אם המשתמש לא מחובר, מוצג לחצן הכניסה. אחר כך המשתמש יכול ללחוץ על הכפתור כדי להפעיל את 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_KEY ואת YOUR_CLIENT_ID במפתח Maps API ובמזהה לקוח OAuth שהתקבלו בשלב דרישות מוקדמות (אפשר ללחוץ על placeholders האלה בקוד שלמטה כדי להוסיף אותם באופן אוטומטי). מחליפים גם את 'my-project' במזהה הפרויקט ב-Google Cloud.

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