مقدمة عن البرمجة الوظيفية
يستخدم Earth Engine نظام معالجة موازية لإجراء العمليات الحسابية على عدد كبير من الأجهزة. لإتاحة هذه المعالجة، تستفيد منصة Earth Engine من الأساليب العادية التي يشيع استخدامها في اللغات الوظيفية، مثل الشفافية المرجعية والتقييم الكسول، لتحقيق تحسين كبير وزيادة الكفاءة.
المفهوم الرئيسي الذي يميّز البرمجة الوظيفية عن البرمجة الإجرائية هو عدم وجود آثار جانبية. يعني ذلك أنّ الدوال التي تكتبها لا تعتمد على بيانات خارج الدالة أو تعدّلها. كما سترى في الأمثلة أدناه، من الممكن إعادة هيكلة مشكلتك بحيث يمكن حلها باستخدام دوال بدون آثار جانبية، وهي أكثر ملاءمة للتنفيذ بالتوازي.
حلقات التكرار For
لا ننصح باستخدام حلقات for في Earth Engine. يمكن تحقيق النتائج نفسها باستخدام عملية map() حيث تحدّد دالة يمكن تطبيقها بشكل مستقل على كل عنصر. يتيح ذلك للنظام توزيع المعالجة على أجهزة مختلفة.
يوضّح المثال أدناه كيفية أخذ قائمة أرقام وإنشاء قائمة أخرى تحتوي على مربّعات كل رقم باستخدام map():
أداة تعديل الرموز (JavaScript)
// This generates a list of numbers from 1 to 10. var myList = ee.List.sequence(1, 10); // The map() operation takes a function that works on each element independently // and returns a value. You define a function that can be applied to the input. var computeSquares = function(number) { // We define the operation using the EE API. return ee.Number(number).pow(2); }; // Apply your function to each item in the list by using the map() function. var squares = myList.map(computeSquares); print(squares); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
شروط If/Else
من المشاكل الشائعة الأخرى التي يواجهها المستخدمون الجدد الذين اعتادوا على نموذج البرمجة الإجرائية هي الاستخدام الصحيح لعوامل التشغيل الشرطية if/else في Earth Engine. ومع أنّ واجهة برمجة التطبيقات توفّر خوارزمية ee.Algorithms.If()، ننصح بشدة بعدم استخدامها، بل استخدام نهج أكثر فاعلية يعتمد على map() والفلاتر.
يستخدم Earth Engine
التنفيذ المؤجَّل، ما يعني أنّه يتم تأخير تقييم التعبير إلى أن تصبح قيمته
المحقَّقة مطلوبة فعليًا. في بعض الحالات، سيقيّم هذا النوع من نماذج التنفيذ
كلاً من البدائل الصحيحة والخاطئة لعبارة ee.Algorithms.If(). وقد يؤدي ذلك إلى زيادة استخدام الذاكرة والعمليات الحسابية، وذلك حسب التعبيرات والموارد المطلوبة لتنفيذها.
لنفترض أنّك تريد حلّ صيغة مختلفة من المثال أعلاه، حيث تكون المهمة هي حساب مربعات الأرقام الفردية فقط. في ما يلي طريقة عملية لحلّ هذه المشكلة بدون استخدام شروط if/else:
أداة تعديل الرموز (JavaScript)
// The following function determines if a number is even or odd. The mod(2) // function returns 0 if the number is even and 1 if it is odd (the remainder // after dividing by 2). The input is multiplied by this remainder so even // numbers get set to 0 and odd numbers are left unchanged. var getOddNumbers = function(number) { number = ee.Number(number); // Cast the input to a Number so we can use mod. var remainder = number.mod(2); return number.multiply(remainder); }; var newList = myList.map(getOddNumbers); // Remove the 0 values. var oddNumbers = newList.removeAll([0]); var squares = oddNumbers.map(computeSquares); print(squares); // [1, 9, 25, 49, 81]
ينطبق هذا النموذج بشكل خاص عند العمل مع المجموعات. إذا أردت تطبيق خوارزمية مختلفة على المجموعة استنادًا إلى بعض الشروط، فإنّ الطريقة المفضّلة هي فلترة المجموعة أولاً استنادًا إلى الشرط، ثم map() دالة مختلفة لكل مجموعة فرعية. يتيح ذلك للنظام تنفيذ العملية بشكل متوازٍ. على سبيل المثال:
أداة تعديل الرموز (JavaScript)
// Import Landsat 8 TOA collection and filter to 2018 images. var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA') .filterDate('2018-01-01', '2019-01-01'); // Divide the collection into 2 subsets and apply a different algorithm on them. var subset1 = collection.filter(ee.Filter.lt('SUN_ELEVATION', 40)); var subset2 = collection.filter(ee.Filter.gte('SUN_ELEVATION', 40)); // Multiply all images in subset1 collection by 2; // do nothing to subset2 collection. var processed1 = subset1.map(function(image) { return image.multiply(2); }); var processed2 = subset2; // Merge the collections to get a single collection. var final = processed1.merge(processed2); print('Original collection size', collection.size()); print('Processed collection size', final.size());
التكرار التراكمي
قد تحتاج إلى إجراء عملية تسلسلية، حيث يتم استخدام نتيجة كل تكرار في التكرار اللاحق. توفّر منصة Earth Engine طريقة iterate()
لهذه المهام. يُرجى العِلم أنّ iterate() يتم تنفيذه بطريقة تسلسلية،
وبالتالي سيكون بطيئًا في العمليات الكبيرة. استخدِمها فقط عندما لا تتمكّن من استخدام map()
والفلاتر لتحقيق النتيجة المرجوة.
من الأمثلة الجيدة على iterate() إنشاء تسلسل أرقام فيبوناتشي. في هذه الحالة، يكون كل رقم في السلسلة هو مجموع الرقمَين السابقَين. تتطلّب الدالة iterate() وسيطتَين،
وهما دالة (خوارزمية) وقيمة أولية. يتم تمرير قيمتَين إلى الدالة نفسها،
وهما القيمة الحالية في التكرار ونتيجة التكرار السابق. يوضّح المثال التالي كيفية تنفيذ متتالية فيبوناتشي في Earth Engine.
أداة تعديل الرموز (JavaScript)
var algorithm = function(current, previous) { previous = ee.List(previous); var n1 = ee.Number(previous.get(-1)); var n2 = ee.Number(previous.get(-2)); return previous.add(n1.add(n2)); }; // Compute 10 iterations. var numIteration = ee.List.repeat(1, 10); var start = [0, 1]; var sequence = numIteration.iterate(algorithm, start); print(sequence); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
بعد أن أصبحت لديك معرفة جيدة بمفاهيم JavaScript، يمكنك الاطّلاع على دليل واجهة برمجة التطبيقات للحصول على مقدّمة عن وظائف البيانات الجغرافية المكانية في واجهة برمجة تطبيقات Earth Engine.