Quickstart

يوضّح هذا الدليل كيف يمكنك البدء بسرعة في إرسال طلبات بحث إلى Earth Engine REST API من Python باستخدام Google Colab. تنطبق المفاهيم نفسها على الوصول إلى واجهة برمجة التطبيقات من لغات وبيئات أخرى.

ملاحظة: تحتوي واجهة REST API على ميزات جديدة ومتقدّمة قد لا تكون مناسبة لجميع المستخدمين. إذا كنت تستخدم Earth Engine للمرة الأولى، يُرجى البدء بقراءة دليل JavaScript.

قبل البدء

اتّبِع هذه التعليمات لإجراء ما يلي:

  • ضبط أذونات الوصول إلى Earth Engine
  • إنشاء حساب خدمة

إعداد ورقة ملاحظات Colab

إذا كنت ستنفّذ هذا التشغيل السريع من البداية، يمكنك إنشاء ورقة ملاحظات Colab جديدة من خلال النقر على ورقة ملاحظات جديدة من صفحة بدء Colab وإدخال نماذج الرموز أدناه في خلية رمز جديدة. تم تثبيت Cloud SDK مسبقًا في Colab. ويشمل ذلك أداة سطر الأوامر gcloud التي يمكنك استخدامها لإدارة الخدمات السحابية. يمكنك بدلاً من ذلك تشغيل ورقة الملاحظات التجريبية من الزر في بداية هذه الصفحة.

المصادقة على Google Cloud

أول ما عليك فعله هو تسجيل الدخول حتى تتمكّن من إرسال طلبات مصادَق عليها إلى Google Cloud.

في Colab، يمكنك تشغيل:

PROJECT = 'my-project'
!gcloud auth login --project {PROJECT}

(أو إذا كنت تنفّذ الأمر محليًا، من سطر الأوامر، بافتراض أنّك ثبّت Cloud SDK:)

PROJECT='my-project'
gcloud auth login --project $PROJECT

اقبَل خيار تسجيل الدخول باستخدام حساب المستخدم على Google وأكمِل عملية تسجيل الدخول.

الحصول على ملف مفتاح خاص لحساب الخدمة

قبل أن تتمكّن من استخدام حساب الخدمة للمصادقة، عليك تنزيل ملف مفتاح خاص. لإجراء ذلك في Colab، عليك تنزيل الملف إلى الجهاز الافتراضي لورقة الملاحظات باتّباع الخطوات التالية:

SERVICE_ACCOUNT='foo-name@project-name.iam.gserviceaccount.com'
KEY = 'my-secret-key.json'
!gcloud iam service-accounts keys create {KEY} --iam-account {SERVICE_ACCOUNT}

أو بدلاً من ذلك من سطر الأوامر:

SERVICE_ACCOUNT='foo-name@project-name.iam.gserviceaccount.com'
KEY='my-secret-key.json'
gcloud iam service-accounts keys create $KEY --iam-account $SERVICE_ACCOUNT

الوصول إلى بيانات الاعتماد واختبارها

أنت الآن جاهز لإرسال طلب البحث الأول إلى Earth Engine API. استخدِم المفتاح الخاص للحصول على بيانات الاعتماد. استخدِم بيانات الاعتماد لإنشاء جلسة معتمَدة لإرسال طلبات HTTP. يمكنك إدخال هذا الرمز في خلية رمز جديدة في دفتر ملاحظات Colab. (إذا كنت تستخدم سطر الأوامر، عليك التأكّد من تثبيت المكتبات الضرورية).

from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account

credentials = service_account.Credentials.from_service_account_file(KEY)
scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/cloud-platform'])

session = AuthorizedSession(scoped_credentials)

url = 'https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/LANDSAT'

response = session.get(url)

from pprint import pprint
import json
pprint(json.loads(response.content))

في حال ضبط كل شيء بشكل صحيح، سيؤدي تشغيل هذا الأمر إلى ظهور مخرجات على النحو التالي:

{'id': 'LANDSAT',
 'name': 'projects/earthengine-public/assets/LANDSAT',
 'type': 'FOLDER'}

اختيار مجموعة بيانات

يمكنك البحث عن مجموعات البيانات المتاحة واستكشافها باستخدام محرّر الرموز البرمجية في Earth Engine على code.earthengine.google.com. لنبحث عن بعض بيانات Sentinel 2. (إذا كانت هذه هي المرة الأولى التي تستخدم فيها "محرّر الأكواد"، سيُطلب منك منح الإذن بالوصول إلى Earth Engine نيابةً عنك عند تسجيل الدخول). في "محرّر الرموز"، ابحث عن "sentinel" في مربّع البحث في أعلى الصفحة. تظهر العديد من مجموعات بيانات الصور النقطية:

انقر على "Sentinel-2: أداة التصوير المتعدد الأطياف (MSI)، المستوى 1C":

تتضمّن صفحات أوصاف مجموعات البيانات، مثل هذه الصفحة، المعلومات الأساسية التي تحتاج إليها لاستخدام أي مجموعة بيانات في "كتالوج البيانات العامة" في Earth Engine، بما في ذلك وصف موجز لمجموعة البيانات وروابط إلى مقدّم البيانات للحصول على تفاصيل إضافية ومعلومات حول أي قيود على الاستخدام قد تنطبق على مجموعة البيانات ومعرّف مجموعة البيانات في Earth Engine.

في هذه الحالة، نرى على الجانب الأيسر من النافذة أنّ هذه مادة عرض لمجموعة صور ومسارها هو COPERNICUS/S2.

طلب البحث عن صور معيّنة

تتضمّن مجموعة بيانات Sentinel-2 أكثر من مليوني صورة تغطي العالم من عام 2015 حتى الآن. لنُصدر طلب بحث projects.assets.listImages بشأن مجموعة الصور للعثور على بعض البيانات من نيسان (أبريل) 2017، مع غطاء سحابي منخفض يتضمّن نقطة معيّنة في ماونتن فيو، كاليفورنيا.

import urllib

coords = [-122.085, 37.422]

project = 'projects/earthengine-public'
asset_id = 'COPERNICUS/S2'
name = '{}/assets/{}'.format(project, asset_id)
url = 'https://earthengine.googleapis.com/v1alpha/{}:listImages?{}'.format(
  name, urllib.parse.urlencode({
    'startTime': '2017-04-01T00:00:00.000Z',
    'endTime': '2017-05-01T00:00:00.000Z',
    'region': '{"type":"Point", "coordinates":' + str(coords) + '}',
    'filter': 'CLOUDY_PIXEL_PERCENTAGE < 10',
}))

response = session.get(url)
content = response.content

for asset in json.loads(content)['images']:
    id = asset['id']
    cloud_cover = asset['properties']['CLOUDY_PIXEL_PERCENTAGE']
    print('%s : %s' % (id, cloud_cover))

يبحث هذا النص البرمجي في المجموعة عن الصور المطابقة، ويفك ترميز استجابة JSON الناتجة، ويعرض رقم تعريف مادة العرض وغطاء السحابة لكل مادة عرض صورة مطابقة. يجب أن تبدو المخرجات على النحو التالي:

COPERNICUS/S2/20170420T184921_20170420T190203_T10SEG : 4.3166
COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG : 0

من الواضح أنّ هناك صورتَين فوق هذه النقطة تم التقاطهما في هذا الشهر وتغطيهما سحب منخفضة.

فحص صورة معيّنة

يبدو أنّ إحدى الصور المتطابقة لا تتضمّن أي غطاء سحابي تقريبًا. لنلقِ نظرة عن كثب على مادة العرض هذه التي تحمل رقم التعريف COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG. يُرجى العِلم أنّ جميع مواد عرض الكتالوج العلني تنتمي إلى المشروع earthengine-public. في ما يلي مقتطف من رمز Python البرمجي سيُصدر طلب بحث projects.assets.get لجلب تفاصيل مادة العرض المحدّدة هذه حسب رقم التعريف، وطباعة نطاقات البيانات المتاحة، وطباعة معلومات أكثر تفصيلاً حول النطاق الأول:

asset_id = 'COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG'
name = '{}/assets/{}'.format(project, asset_id)
url = 'https://earthengine.googleapis.com/v1alpha/{}'.format(name)

response = session.get(url)
content = response.content

asset = json.loads(content)
print('Band Names: %s' % ','.join(band['id'] for band in asset['bands']))
print('First Band: %s' % json.dumps(asset['bands'][0], indent=2, sort_keys=True))

يجب أن تبدو المخرجات على النحو التالي:

Band Names: B1,B2,B3,B4,B5,B6,B7,B8,B8A,B9,B10,B11,B12,QA10,QA20,QA60
First Band: {
  "dataType": {
    "precision": "INTEGER",
    "range": {
      "max": 65535
    }
  },
  "grid": {
    "affineTransform": {
      "scaleX": 60,
      "scaleY": -60,
      "translateX": 499980,
      "translateY": 4200000
    },
    "crsCode": "EPSG:32610",
    "dimensions": {
      "height": 1830,
      "width": 1830
    }
  },
  "id": "B1",
  "pyramidingPolicy": "MEAN"
}

تتوافق قائمة نطاقات البيانات مع ما رأيناه سابقًا في وصف مجموعة البيانات. يمكننا ملاحظة أنّ مجموعة البيانات هذه تتضمّن بيانات أعداد صحيحة ذات 16 بت في نظام الإحداثيات EPSG:32610 أو منطقة UTM 10N. تحتوي هذه الحزمة الأولى على المعرّف B1 ودقة تبلغ 60 مترًا لكل بكسل. يقع أصل الصورة في الموضع (499980,4200000) في نظام الإحداثيات هذا.

تشير القيمة السالبة affineTransform.scaleY إلى أنّ نقطة الأصل تقع في الزاوية الشمالية الغربية من الصورة، كما هو الحال عادةً: تتوافق مؤشرات البكسل المتزايدة y مع الإحداثيات المكانية المتناقصة y (الاتجاه جنوبًا).

استرداد قيم وحدات البكسل

لنرسل طلب بحث projects.assets.getPixels لجلب بعض البيانات من النطاقات العالية الدقة لهذه الصورة. تذكر صفحة وصف مجموعة البيانات أنّ النطاقات B2 وB3 وB4 وB8 تبلغ دقتها 10 أمتار لكل بكسل. يجلب هذا النص البرمجي مربّع البيانات الذي يبلغ حجمه 256×256 بكسل في أعلى اليمين من تلك النطاقات الأربعة. يؤدي تحميل البيانات بتنسيق numpy NPY إلى تسهيل فك ترميز الردّ إلى مصفوفة بيانات Python.

import numpy
import io

name = '{}/assets/{}'.format(project, asset_id)
url = 'https://earthengine.googleapis.com/v1alpha/{}:getPixels'.format(name)
body = json.dumps({
    'fileFormat': 'NPY',
    'bandIds': ['B2', 'B3', 'B4', 'B8'],
    'grid': {
        'affineTransform': {
            'scaleX': 10,
            'scaleY': -10,
            'translateX': 499980,
            'translateY': 4200000,
        },
        'dimensions': {'width': 256, 'height': 256},
    },
})

pixels_response = session.post(url, body)
pixels_content = pixels_response.content

array = numpy.load(io.BytesIO(pixels_content))
print('Shape: %s' % (array.shape,))
print('Data:')
print(array)

يجب أن تبدو المخرجات على النحو التالي:

Shape: (256, 256)
Data:
[[( 899, 586, 351, 189) ( 918, 630, 501, 248) (1013, 773, 654, 378) ...,
  (1014, 690, 419, 323) ( 942, 657, 424, 260) ( 987, 691, 431, 315)]
 [( 902, 630, 541, 227) (1059, 866, 719, 429) (1195, 922, 626, 539) ...,
  ( 978, 659, 404, 287) ( 954, 672, 426, 279) ( 990, 678, 397, 304)]
 [(1050, 855, 721, 419) (1257, 977, 635, 569) (1137, 770, 400, 435) ...,
  ( 972, 674, 421, 312) (1001, 688, 431, 311) (1004, 659, 378, 284)]
 ...,
 [( 969, 672, 375, 275) ( 927, 680, 478, 294) (1018, 724, 455, 353) ...,
  ( 924, 659, 375, 232) ( 921, 664, 438, 273) ( 966, 737, 521, 306)]
 [( 920, 645, 391, 248) ( 979, 728, 481, 327) ( 997, 708, 425, 324) ...,
  ( 927, 673, 387, 243) ( 927, 688, 459, 284) ( 962, 732, 509, 331)]
 [( 978, 723, 449, 330) (1005, 712, 446, 314) ( 946, 667, 393, 269) ...,
  ( 949, 692, 413, 271) ( 927, 689, 472, 285) ( 966, 742, 516, 331)]]

لاختيار مجموعة مختلفة من وحدات البكسل من هذه الصورة، ما عليك سوى تحديد affineTransform وفقًا لذلك. تذكَّر أنّ affineTransform يتم تحديده في نظام الإسناد المكاني للإحداثيات الخاص بالصورة. إذا أردت تعديل موقع نقطة الأصل في إحداثيات البكسل بدلاً من ذلك، استخدِم هذه الصيغة البسيطة:

request_origin = image_origin + pixel_scale * offset_in_pixels

إنشاء صورة مصغّرة

يمكننا استخدام آلية مشابهة لإنشاء صورة مصغّرة بتنسيق RGB لهذه الصورة. بدلاً من طلب البيانات بدقتها الأصلية، سنحدّد منطقة وأبعاد صورة بشكلٍ صريح. للحصول على صورة مصغّرة للصورة بأكملها، يمكننا استخدام شكل هندسي لبصمة الصورة كمنطقة الطلب. أخيرًا، من خلال تحديد نطاقات الصور الحمراء والخضراء والزرقاء ونطاق مناسب من قيم البيانات، يمكننا الحصول على صورة مصغّرة جذابة بتنسيق RGB.

بوضع كل ذلك معًا، يبدو مقتطف رمز Python على النحو التالي (باستخدام أداة عرض الصور IPython في Colab):

url = 'https://earthengine.googleapis.com/v1alpha/{}:getPixels'.format(name)
body = json.dumps({
    'fileFormat': 'PNG',
    'bandIds': ['B4', 'B3', 'B2'],
    'region': asset['geometry'],
    'grid': {
        'dimensions': {'width': 256, 'height': 256},
    },
    'visualizationOptions': {
        'ranges': [{'min': 0, 'max': 3000}],
    },
})

image_response = session.post(url, body)
image_content = image_response.content

from IPython.display import Image
Image(image_content)

إليك الصورة المصغّرة الناتجة: