يتم تشغيل الخوارزميات التي تنشئها في Earth Engine في السحابة الحاسبة من Google، ويتم توزيعها على العديد من أجهزة الكمبيوتر. يمكن أن يكون تصحيح الأخطاء صعبًا لأنّه يمكن أن تحدث أخطاء في الرمز البرمجي من جهة العميل أو في تنفيذ التعليمات المبرمَجة من جهة الخادم، ويمكن أن تنتج عن مشاكل في التوسّع بالإضافة إلى أخطاء نحوية أو منطقية. إنّ أجزاء البرنامج التي يتم تشغيلها في مكان ما في السحابة الإلكترونية غير متاحة للفحص، ما لم تطلبها. يعرض هذا المستند استراتيجيات debugging والأدوات والحلول لمساعدتك في حلّ الأخطاء الشائعة وdebugging النصوص البرمجية في Earth Engine.
أخطاء البنية
تحدث أخطاء البنية عندما تنتهك رمزك قواعد لغة البرمجة (إما JavaScript أو Python في Earth Engine). تمنع هذه الأخطاء تنفيذ الرمز البرمجي ويتم عادةً رصدها قبل التنفيذ. إذا واجهت خطأ في البنية، راجِع بعناية السطر المميّز أو رسالة الخطأ، واستشِر مراجع مثل مرجع لغة Python أو دليل أسلوب JavaScript من Google. يمكن أن يساعدك أيضًا مدقّق الرموز البرمجية في تحديد هذه المشاكل وحلّها.
الأخطاء من جهة العميل
على الرغم من أنّ الرمز البرمجي صحيح من حيث البنية، قد تكون هناك أخطاء مرتبطة بتناسق النص البرمجي أو منطقه. توضّح الأمثلة التالية أخطاء من استخدام متغيّر وطريقة غير موجودَين.
خطأ: هذا الرمز غير صالح.
محرِّر الرموز البرمجية (JavaScript)
// Load a Sentinel-2 image. var image = ee.Image('USGS/SRTMGL1_003'); // Error: "bandNames" is not defined in this scope. var display = image.visualize({bands: bandNames, min: 0, max: 9000}); // Error: image.selfAnalyze is not a function var silly = image.selfAnalyze();
import ee import geemap.core as geemap
Colab (Python)
# Load a Sentinel-2 image. image = ee.Image('USGS/SRTMGL1_003') # NameError: name 'band_names' is not defined. display = image.visualize(bands=band_names, min=0, max=9000) # AttributeError: 'Image' object has no attribute 'selfAnalyze'. silly = image.selfAnalyze()
يُعلمك الخطأ الأول بأنّ المتغيّر bandNames
غير محدّد
في النطاق الذي تتم الإشارة إليه. كحلّ، يمكنك ضبط المتغيّر أو تقديم وسيطة قائمة
للمَعلمة bands
. يوضّح الخطأ الثاني ما يلي:
يحدث عند استدعاء الدالة selfAnalyze()
غير المتوفّرة. بما أنّ
هذه ليست طريقة حقيقية للصور، يُعلمك الخطأ بأنّها ليست دالة. وفي كلتا الحالتَين، يصف الخطأ المشكلة.
تحويل نوع عنصر غير معروف
قد ينتج خطأ "...is not a function
" عن عدم معرفة Earth Engine
لنوع المتغيّر. تنتج مظاهر هذه المشكلة الشائعة عن:
- إجراء عملية على عنصر تم إرجاعه بواسطة
first()
(نوع العناصر في المجموعة غير معروف) - إجراء عملية على عنصر يعرضه
get()
(نوع العنصر المخزّن في السمة غير معروف) - إجراء عملية على وسيطة دالة (في الدالة) عندما يكون نوع الوسيطة غير معروف
في ما يلي مثال على ذلك:
خطأ: هذا الرمز غير صالح.
محرِّر الرموز البرمجية (JavaScript)
var collection = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017'); // Error: collection.first(...).area is not a function var area = collection.first().area();
import ee import geemap.core as geemap
Colab (Python)
collection = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017') # AttributeError: 'Element' object has no attribute 'area'. area = collection.first().area()
الحلّ في جميع الحالات هو تحويل العنصر من النوع غير المعروف باستخدام أسلوب الإنشاء
من النوع المعروف. في ما يلي مثال على كيفية حلّ المشكلة:
ee.Feature
:
الحل: استخدام جهاز بث
محرِّر الرموز البرمجية (JavaScript)
var area = ee.Feature(collection.first()).area();
import ee import geemap.core as geemap
Colab (Python)
area = ee.Feature(collection.first()).area()
(يُرجى العلم أنّه يمكنك استدعاء أي طريقة على Element
بأمان
لأنّ هذا هو ما يعتقده Earth Engine).
تجنُّب خلط وظائف العميل والخادم
أما المثال التالي، فهو أقل وضوحًا:
خطأ: لا يؤدي هذا الرمز إلى تنفيذ ما تريده
محرِّر الرموز البرمجية (JavaScript)
// Don't mix EE objects and JavaScript objects. var image = ee.Image('USGS/SRTMGL1_003'); var nonsense = image + 2; // You can print this, but it's not what you were hoping for. print(nonsense); // Error: g.eeObject.name is not a function Map.addLayer(nonsense);
import ee import geemap.core as geemap
Colab (Python)
# Don't mix EE objects and Python objects. image = ee.Image('USGS/SRTMGL1_003') nonsense = image + 2 # TypeError: unsupported operand type(s) for +: 'Image' and 'int'. display(nonsense) # TypeError: unsupported operand type(s) for +: 'Image' and 'int'. m = geemap.Map() m.add_layer(nonsense) m
لنفترض أنّ كاتب هذا الرمز البرمجي أراد إضافة 2
إلى كل بكسل في
الصورة، هذه ليست الطريقة الصحيحة لإجراء ذلك. على وجه التحديد، يمزج هذا الرمز بشكلٍ خاطئ بين
عنصر من جهة الخادم (image
) ومُشغِّل من جهة العميل
(+
). وقد تكون النتائج مفاجئة. في الحالة الأولى، ستؤدي طباعة
nonsense
في "محرر رموز JavaScript" إلى تنفيذ العملية المطلوبة
(+
) من خلال تحويل كلّ من image
و2
إلى سلاسل،
ثم تسلسلها. السلسلة الناتجة غير مقصودة (في Python، يتم طرح TypeError).
في الحالة الثانية، عند إضافة nonsense
إلى الخريطة، يتم عرض الخطأ المبهم
g.eeObject.name is not a function
في محرِّر رمز JavaScript
لأنّ العنصر الذي تتم إضافته إلى الخريطة، وهو nonsense
، هو سلسلة، وليس
كائن EE (في Python، يتم طرح TypeError). لتجنُّب النتائج غير المقصودة
والأخطاء غير المفيدة، لا تُمزِج بين عناصر الخادم ودوالّه وعناصر العميل أو الدوالّ الأساسية
أو الدوالّ. الحلّ في هذا المثال هو استخدام دالة خادم.
الحلّ: استخدِم دالة خادم.
محرِّر الرموز البرمجية (JavaScript)
Map.addLayer(image.add(2));
import ee import geemap.core as geemap
Colab (Python)
m = geemap.Map() m.add_layer(image.add(2)) m
اطّلِع على صفحة العميل في مقابل الخادم للحصول على المزيد من التفاصيل.
قفل المتصفّح في "محرر رموز JavaScript"
يمكن أن يتوقّف المتصفّح أو يتم قفله عندما يستغرق تشغيل JavaScript في العميل وقتًا طويلاً جدًا، أو
عند انتظار شيء من Earth Engine. هناك مصدران شائعان لهذا الخطأ، وهما
الحلقات البرمجية و/أو getInfo()
في رمز "محرر رموز JavaScript"، مع
أسوأ حالة وهي getInfo()
داخل حلقة برمجية. يمكن أن تؤدي حلقات For إلى
قفل المتصفّح لأنّ الرمز البرمجي يتم تنفيذه على جهازك. من ناحية أخرى،
getInfo()
يطلب بشكل متزامن نتيجة عملية حسابية من Earth Engine،
ويمنع المعالجة إلى أن يتم استلام النتيجة. إذا استغرقت العملية الحسابية وقتًا طويلاً، قد يؤدي الحظر
إلى قفل المتصفّح. تجنَّب استخدام الحلقات for وgetInfo()
while
أثناء العمل في "محرِّر الرموز". اطّلِع على صفحة
العميل في مقابل الخادم للحصول على المزيد من
التفاصيل.
الأخطاء من جهة الخادم
على الرغم من الاتساق المنطقي في رمز العميل، قد تكون هناك أخطاء لا تظهر إلا في وقت التشغيل على الخادم. يوضّح المثال التالي ما يحدث عند محاولة الحصول على نطاق غير متوفّر.
خطأ: هذا الرمز غير صالح.
محرِّر الرموز البرمجية (JavaScript)
// Load a Sentinel-2 image. var s2image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR'); // Error: Image.select: Pattern 'nonBand' did not match any bands. print(s2image.select(['nonBand']));
import ee import geemap.core as geemap
Colab (Python)
# Load a Sentinel-2 image. s2image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR' ) # EEException: Image.select: Band pattern 'non_band' did not match any bands. print(s2image.select(['non_band']).getInfo())
في هذا المثال، يُعلمك الخطأ بأنّه ما مِن فرقة باسم
nonBand
. الحلّ الواضح على الأرجح هو تحديد اسم فرقة
يوجد. يمكنك الاطّلاع على أسماء المجموعات من خلال طباعة الصورة و
الاطّلاع عليها في وحدة التحكّم، أو من خلال طباعة قائمة أسماء المجموعات التي يعرضها
image.bandNames()
.
عدم القابلية للتغيير
إنّ العناصر من جهة الخادم التي تنشئها في Earth Engine هي
immutable. (أي
ee.Object
هو Object
من جهة الخادم). وهذا يعني أنّه إذا
أردت إجراء تغيير على العنصر، عليك حفظ الحالة التي تم تغييرها في متغيّر
جديد. على سبيل المثال، لن تنجح هذه الطريقة في ضبط خاصية على صورة Sentinel-2:
خطأ: لا يؤدي هذا الرمز إلى تنفيذ ما تريده.
محرِّر الرموز البرمجية (JavaScript)
var s2image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR'); s2image.set('myProperty', 'This image is not assigned to a variable'); // This will not result in an error, but will not find 'myProperty'. print(s2image.get('myProperty')); // null
import ee import geemap.core as geemap
Colab (Python)
s2image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR' ) s2image.set('my_property', 'This image is not assigned to a variable') # This will not result in an error, but will not find 'my_property'. display(s2image.get('my_property')) # None
في هذا المثال، يعرض s2image.set()
نسخة من الصورة التي تحتوي على السمة
الجديدة، ولكن لا يطرأ أي تغيير على الصورة المخزّنة في المتغيّر s2image
. عليك
حفظ الصورة التي تعرضها الدالة s2image.set()
في متغيّر جديد. على سبيل المثال:
الحل: عليك تسجيل النتيجة في متغيّر.
محرِّر الرموز البرمجية (JavaScript)
s2image = s2image.set('myProperty', 'OK'); print(s2image.get('myProperty')); // OK
import ee import geemap.core as geemap
Colab (Python)
s2image = s2image.set('my_property', 'OK') display(s2image.get('my_property')) # OK
الدوالّ المُعرَّفة
هناك سياق آخر لا تتحد فيه دوالّ العميل والخادم، وهو الدوالّ المُحدَّدة. على وجه التحديد، يتم تنفيذ العمليات المحدّدة من خلال الدالة المُعرَّفة في السحابة الإلكترونية،
لذا لن تعمل دوال العميل، مثل getInfo
وExport
(بالإضافة إلى print
وطريقة Map
وChart
في
"محرر رموز JavaScript") في الدوال المُعرَّفة. على سبيل المثال:
خطأ: هذا الرمز غير صالح.
محرِّر الرموز البرمجية (JavaScript)
var collection = ee.ImageCollection('MODIS/006/MOD44B'); // Error: A mapped function's arguments cannot be used in client-side operations var badMap3 = collection.map(function(image) { print(image); return image; });
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('MODIS/006/MOD44B') # Error: A mapped function's arguments cannot be used in client-side operations. bad_map_3 = collection.map(lambda image: print(image.getInfo()))
ينتج هذا الخطأ الغامض إلى حدٍ ما عن العملية التي تستخدمها Earth Engine لتحويل هذا الرمز إلى مجموعة من التعليمات التي يمكن تنفيذها على خوادم Google. لا يمكن استخدام الدوالّ و هياكل التحكّم من جهة العميل لإجراء عمليات على صورة الوسيطة التي تم تمريرها إلى الدوالّ المُعرَّفة. لتجنُّب هذا الخطأ، تجنَّب استخدام الدوالّ من جهة العميل في الدوالّ المُعرَّفة. اطّلِع على صفحة الاختلافات بين العميل والخادم للتعرّف على المزيد من المعلومات عن الفرق بين وظائف العميل والخادم.
تفرض الدوالّ المُحدَّدة متطلبات إضافية. على سبيل المثال، يجب أن تُرجع الدوالّ المُعرَّفة قيمة:
خطأ: هذا الرمز غير صالح.
محرِّر الرموز البرمجية (JavaScript)
var collection = ee.ImageCollection('MODIS/006/MOD44B'); // Error: User-defined methods must return a value. var badMap1 = collection.map(function(image) { // Do nothing. });
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('MODIS/006/MOD44B') # Error: User-defined methods must return a value. bad_map_1 = collection.map(lambda image: None)
الحلّ الواضح على الأرجح هو إرجاع المنتج. ولكن لا يمكنه عرض أي نوع
من المحتوى. على وجه التحديد، يجب أن تعرِض الدوالّ التي تمّ ربطها بعنصر ImageCollection
أو
FeatureCollection
عنصر Image
أو
Feature
. على سبيل المثال، لا يمكنك عرض تاريخ من دالة تمّ ربطها
بعنصر ImageCollection
:
خطأ: هذا الرمز غير صالح.
محرِّر الرموز البرمجية (JavaScript)
var collection = ee.ImageCollection('MODIS/006/MOD44B'); var badMap2 = collection.map(function(image) { return image.date(); }); // Error: Collection.map: A mapped algorithm must return a Feature or Image. print(badMap2);
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('MODIS/006/MOD44B') bad_map_2 = collection.map(lambda image: image.date()) # EEException: Collection.map: # A mapped algorithm must return a Feature or Image. print(bad_map_2.getInfo())
لتجنّب ذلك، أعِد عرض صورة الإدخال مع مجموعة جديدة من السمات. بعد ذلك، إذا كنت بحاجة إلى قائمة
بتواريخ الصور في المجموعة، يمكنك استخدام aggregate_array()
:
الحلّ: ضبط موقع
محرِّر الرموز البرمجية (JavaScript)
var collection = ee.ImageCollection('MODIS/006/MOD44B'); var okMap2 = collection.map(function(image) { return image.set('date', image.date()); }); print(okMap2); // Get a list of the dates. var datesList = okMap2.aggregate_array('date'); print(datesList);
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('MODIS/006/MOD44B') ok_map_2 = collection.map(lambda image: image.set('date', image.date())) print(ok_map_2.getInfo()) # Get a list of the dates. dates_list = ok_map_2.aggregate_array('date') print(dates_list.getInfo())
الأخطاء الإجرائية
تم تطبيق النمط على صورة لا تحتوي على نطاقات.
يعني الخطأ "Pattern 'my_band' was applied to an Image with no bands"
أنّ هناك طلبًا ee.Image.select()
لصورة
تحتوي على قائمة نطاقات فارغة. في ما يلي الإجراءات التي يمكنك اتّخاذها لحلّ هذه المشكلة:
- إذا تم إنشاء الصورة من ImageCollection باستخدام مُخفِّض أو
باستخدام طلبَي
first()
أوtoBands()
، تأكَّد من أنّ المجموعة المصدر ليست فارغة. - إذا تم إنشاء الصورة من قاموس باستخدام
ee.Dictionary().toImage()
، تأكَّد من أنّ القاموس ليس فارغًا. - إذا كانت الصورة مستقلة، تأكَّد من أنّها تحتوي على بيانات (وألّا تكون فقط
ee.Image(0)
).
أخطاء التوسّع
على الرغم من أنّ النص البرمجي قد يكون صحيحًا من الناحية النحوية، بدون أخطاء منطقية، ويمثّل مجموعة صالحة من التعليمات للخادم، إلا أنّه عند إجراء العمليات الحسابية بشكل موازٍ وتنفيذها، قد تكون العناصر الناتجة كبيرة جدًا أو كثيرة جدًا أو قد تستغرق وقتًا طويلاً جدًا لحسابها. في هذه الحالة، ستظهر لك رسالة خطأ تفيد بأنّه لا يمكن توسيع نطاق الخوارزمية. وتعدّ هذه الأخطاء بشكل عام من أصعب الأخطاء التي يصعب تشخيصها وحلّها. تشمل أمثلة هذا النوع من الأخطاء ما يلي:
- انتهت مهلة الحساب
- عدد كبير جدًا من عمليات التجميع المتزامنة
- تم تجاوز الحد الأقصى المسموح به لمساحة الذاكرة المخصّصة للمستخدم
- حدث خطأ داخلي.
سيتيح لك تحسين إمكانية توسيع نطاق رمزك البرمجي الحصول على النتائج بشكل أسرع، كما سيؤدي إلى تحسين
توفّر موارد الحوسبة لجميع المستخدمين. تتم مناقشة كل نوع من أنواع الأخطاء في
الأقسام التالية، بعد تقديم ملاحظة موجزة عن reduceRegion()
، وهي دالة
شائعة الاستخدام تشتهر بقدرتها على التسبب في كل أنواع أخطاء التوسيع.
reduceRegion()
على الرغم من أنّ reduceRegion()
يستهلك بشغف عددًا كافيًا من البكسلات لرصد
مجموعة متنوعة من الأخطاء المثيرة، هناك أيضًا مَعلمات مخصّصة للتحكّم في عملية حساب
القيمة، ما يتيح لك معالجة الأخطاء. على سبيل المثال، إليك مثالاً على
التخفيضات غير المُقترَحة:
خطأ: هذا الرمز غير صالح.
محرِّر الرموز البرمجية (JavaScript)
var absurdComputation = ee.Image(1).reduceRegion({ reducer: 'count', geometry: ee.Geometry.Rectangle([-180, -90, 180, 90], null, false), scale: 100, }); // Error: Image.reduceRegion: Too many pixels in the region. // Found 80300348117, but only 10000000 allowed. print(absurdComputation);
import ee import geemap.core as geemap
Colab (Python)
absurd_computation = ee.Image(1).reduceRegion( reducer='count', geometry=ee.Geometry.Rectangle([-180, -90, 180, 90], None, False), scale=100, ) # EEException: Image.reduceRegion: Too many pixels in the region. # Found 80300348117, but only 10000000 allowed. print(absurd_computation.getInfo())
هذا المثال المضحك مخصّص للتوضيح فقط. الغرض من هذا الخطأ هو أن نسألك
ما إذا كنت حقًا تريد تقليل 80300348117 (أي 80 مليار)
بكسل. إذا لم يكن الأمر كذلك، عليك زيادة scale
(حجم البكسل بالمتر) وفقًا لذلك، أو
ضبط bestEffort
على true لإعادة احتساب مقياس أكبر تلقائيًا. يُرجى الاطّلاع على صفحة
reduceRegion()
للحصول على
المزيد من التفاصيل عن هذه المَعلمات.
انتهت مهلة الحساب
لنفترض أنّك بحاجة إلى كل هذه البكسلات في عملية الحساب. إذا كان الأمر كذلك، يمكنك زيادة المَعلمة
maxPixels
للسماح بنجاح عملية الحساب. ومع ذلك، لن يستغرق Earth Engine سوى
بعض الوقت لإكمال عملية الحساب. نتيجةً لذلك، قد يتم طرح خطأ
"انتهت مهلة الحساب":
خطأ: لا تفعل ذلك.
محرِّر الرموز البرمجية (JavaScript)
var ridiculousComputation = ee.Image(1).reduceRegion({ reducer: 'count', geometry: ee.Geometry.Rectangle([-180, -90, 180, 90], null, false), scale: 100, maxPixels: 1e11 }); // Error: Computation timed out. print(ridiculousComputation);
import ee import geemap.core as geemap
Colab (Python)
ridiculous_computation = ee.Image(1).reduceRegion( reducer='count', geometry=ee.Geometry.Rectangle([-180, -90, 180, 90], None, False), scale=100, maxPixels=int(1e11), ) # Error: Computation timed out. print(ridiculous_computation.getInfo())
يعني هذا الخطأ أنّ Earth Engine انتظرت حوالي خمس دقائق قبل إيقاف عملية حسابية. تسمح عملية التصدير لخدمة Earth Engine بإجراء العمليات الحسابية في بيئة
تتضمّن أوقات تشغيل مسموح بها أطول (ولكن ليس بمزيد من الذاكرة). بما أنّ القيمة المعروضة
من reduceRegion()
هي قاموس، يمكنك استخدام القاموس لضبط
سمات عنصر بهندسة فارغة:
جيد، استخدِم Export
.
محرِّر الرموز البرمجية (JavaScript)
Export.table.toDrive({ collection: ee.FeatureCollection([ ee.Feature(null, ridiculousComputation) ]), description: 'ridiculousComputation', fileFormat: 'CSV' });
import ee import geemap.core as geemap
Colab (Python)
task = ee.batch.Export.table.toDrive( collection=ee.FeatureCollection([ee.Feature(None, ridiculous_computation)]), description='ridiculous_computation', fileFormat='CSV', ) # task.start()
عدد كبير جدًا من عمليات التجميع المتزامنة
يشير جزء "عمليات التجميع" من هذا الخطأ إلى العمليات التي يتم توزيعها على أجهزة متعددة (مثل عمليات التقليل التي تمتد على أكثر من مربّع واحد). تفرض أداة Earth Engine حدودًا لمنع تنفيذ عدد كبير جدًا من عمليات التجميع هذه بشكل متزامن. في هذا المثال، يتم تشغيل خطأ "عمليات تجميع متزامنة كثيرة جدًا" بسبب تقليل ضمن خريطة:
خطأ: لا تفعل ذلك.
محرِّر الرموز البرمجية (JavaScript)
var collection = ee.ImageCollection('LANDSAT/LT05/C02/T1') .filterBounds(ee.Geometry.Point([-123, 43])); var terribleAggregations = collection.map(function(image) { return image.set(image.reduceRegion({ reducer: 'mean', geometry: image.geometry(), scale: 30, maxPixels: 1e9 })); }); // Error: Quota exceeded: Too many concurrent aggregations. print(terribleAggregations);
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('LANDSAT/LT05/C02/T1').filterBounds( ee.Geometry.Point([-123, 43]) ) def apply_mean_aggregation(image): return image.set( image.reduceRegion( reducer='mean', geometry=image.geometry(), scale=30, maxPixels=int(1e9), ) ) terrible_aggregations = collection.map(apply_mean_aggregation) # EEException: Computation timed out. print(terrible_aggregations.getInfo())
بافتراض أنّ الغرض من هذا الرمز هو الحصول على إحصاءات الصور لكل صورة، أحد
الحلول المحتملة هي Export
النتيجة. على سبيل المثال، باستخدام حقيقة
أنّ ImageCollection
هو أيضًا FeatureCollection
، يمكن تصدير
البيانات الوصفية المرتبطة بالصور كجدول:
جيد، استخدِم Export
.
محرِّر الرموز البرمجية (JavaScript)
Export.table.toDrive({ collection: terribleAggregations, description: 'terribleAggregations', fileFormat: 'CSV' });
import ee import geemap.core as geemap
Colab (Python)
task = ee.batch.Export.table.toDrive( collection=terrible_aggregations, description='terrible_aggregations', fileFormat='CSV', ) # task.start()
تم تجاوز الحد الأقصى المسموح به لمساحة الذاكرة المخصّصة للمستخدم
من الطرق التي يتم من خلالها تنفيذ خوارزمياتك بشكل موازٍ في Earth Engine هي تقسيم المدخلات إلى شرائح، وتنفيذ العملية الحسابية نفسها بشكل منفصل على كل شريحة، ثم دمج النتائج. ونتيجةً لذلك، يجب أن تتوافق جميع المدخلات اللازمة لحساب مربّع الإخراج مع المساحة المتوفرة في الذاكرة. على سبيل المثال، عندما تكون الإدخال صورة تتضمّن العديد من النطاقات، قد يؤدي ذلك إلى استخدام الكثير من الذاكرة في حال استخدام جميع النطاقات في العمليات الحسابية. للتوضيح، يستخدم هذا المثال مساحة كبيرة جدًا من الذاكرة من خلال فرض إدراج مجموعة كاملة من الصور في مربّع (بدون داعٍ):
خطأ: لا تفعل ذلك.
محرِّر الرموز البرمجية (JavaScript)
var bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']; var memoryHog = ee.ImageCollection('LANDSAT/LT05/C02/T1').select(bands) .toArray() .arrayReduce(ee.Reducer.mean(), [0]) .arrayProject([1]) .arrayFlatten([bands]) .reduceRegion({ reducer: 'mean', geometry: ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale: 1, bestEffort: true, }); // Error: User memory limit exceeded. print(memoryHog);
import ee import geemap.core as geemap
Colab (Python)
bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'] memory_hog = ( ee.ImageCollection('LANDSAT/LT05/C02/T1') .select(bands) .toArray() .arrayReduce(ee.Reducer.mean(), [0]) .arrayProject([1]) .arrayFlatten([bands]) .reduceRegion( reducer=ee.Reducer.mean(), geometry=ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale=1, bestEffort=True, ) ) # EEException: User memory limit exceeded. print(memory_hog.getInfo())
يوضّح هذا الرمز البرمجي السيئ جدًا سببًا واحدًا لعدم استخدام الصفائف إلا إذا كنت بحاجة إليها حقًا (راجِع أيضًا قسم "تجنُّب تحويل النوع بدون داعٍ"). عند تحويل هذه المجموعة إلى صفيف ضخم، يجب تحميل الصفيف في الذاكرة دفعة واحدة. وبما أنّها سلسلة طويلة من الصور، تكون الصفيف كبيرة ولن تتسع في الذاكرة.
أحد الحلول المحتملة هو ضبط المَعلمة tileScale
على قيمة
أعلى. تؤدي القيم الأعلى لمقياس المربّع إلى تصغير المربّعات بمقدار
tileScale^2
. على سبيل المثال، يسمح ما يلي بنجاح عملية الحساب:
محرِّر الرموز البرمجية (JavaScript)
var bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']; var smallerHog = ee.ImageCollection('LANDSAT/LT05/C02/T1').select(bands) .toArray() .arrayReduce(ee.Reducer.mean(), [0]) .arrayProject([1]) .arrayFlatten([bands]) .reduceRegion({ reducer: 'mean', geometry: ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale: 1, bestEffort: true, tileScale: 16 }); print(smallerHog);
import ee import geemap.core as geemap
Colab (Python)
bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'] smaller_hog = ( ee.ImageCollection('LANDSAT/LT05/C02/T1') .select(bands) .toArray() .arrayReduce(ee.Reducer.mean(), [0]) .arrayProject([1]) .arrayFlatten([bands]) .reduceRegion( reducer=ee.Reducer.mean(), geometry=ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale=1, bestEffort=True, tileScale=16, ) ) print(smaller_hog.getInfo())
ومع ذلك، فإنّ الحلّ المفضّل هو عدم استخدام المصفوفات بدون داعٍ، حتى لا تحتاج إلى
العبث بالعنصر tileScale
على الإطلاق:
جيد، تجنَّب استخدام الصفائف.
محرِّر الرموز البرمجية (JavaScript)
var bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']; var okMemory = ee.ImageCollection('LANDSAT/LT05/C02/T1').select(bands) .mean() .reduceRegion({ reducer: 'mean', geometry: ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale: 1, bestEffort: true, }); print(okMemory);
import ee import geemap.core as geemap
Colab (Python)
bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'] ok_memory = ( ee.ImageCollection('LANDSAT/LT05/C02/T1') .select(bands) .mean() .reduceRegion( reducer=ee.Reducer.mean(), geometry=ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale=1, bestEffort=True, ) ) print(ok_memory.getInfo())
يجب عدم ضبط tileScale
ما لم يكن ذلك ضروريًا لحلّ خطأ في الذاكرة، لأنّ الأقسام الأصغر تؤدي أيضًا إلى زيادة في وقت التشغيل المخصص للمهام الموازية.
الأخطاء الداخلية
قد يظهر لك خطأ بالشكل التالي:
إذا ظهرت لك رسالة الخطأ هذه، انقر على الرابط "الإبلاغ عن خطأ" الذي يظهر في وحدة تحكّم محرِّر JavaScript. يمكنك أيضًا إرسال ملاحظات من الزر مساعدة. يمكن أن ينتج هذا الخطأ عن أخطاء منطقية في النص البرمجي لا تظهر إلا عند التشغيل أو مشكلة في الوظائف الداخلية لخدمة Earth Engine. في كلٍّ من الحالتَين، لا يقدّم الخطأ أي معلومات، ويجب الإبلاغ عنه ليتم حلّه.
تتضمّن الأخطاء الداخلية معرّف request
، مثل ما يلي:
تعمل هذه السلاسل كمعرّفات فريدة لمساعدة فريق Earth Engine في تحديد مشاكل محدّدة. أدرِج هذه السلسلة في تقارير الأخطاء.
طرق تصحيح الأخطاء
لقد كتبت رمزًا لتحليلك ونفّذته وظهر خطأ. فماذا أفعل الآن؟ يصف هذا القسم تقنيات تصحيح الأخطاء العامة لتحديد المشكلة وحلّها.
فحص المتغيّرات وطبقات الخريطة
لنفترض أنّ لديك تحليلاً معقّدًا جدًا يؤدي إلى ظهور خطأ. إذا لم يكن من الواضح مصدر الخطأ، من الاستراتيجيات الأولية الجيدة طباعة أو عرض الكائنات الوسيطة وفحصها للتأكّد من أنّ بنية الكائن متّسقة مع المنطق في النص البرمجي. على وجه التحديد، يمكنك فحص قيم البكسل للطبقات التي تمت إضافتها إلى الخريطة باستخدام "أداة فحص geemap" أو "أداة فحص الرموز البرمجية". إذا كنت بصدد طباعة عنصر معيّن، احرص على توسيع خصائصه باستخدام الرموز البرمجية (▶). في ما يلي بعض النقاط التي يجب التحقّق منها:
- أسماء المجموعات هل تتطابق أسماء نطاقات الصور مع الرمز؟
- قيم وحدات البكسل هل تتضمّن بياناتك النطاق الصحيح؟ هل تم حجبه بشكل مناسب؟
- فارغة. هل هناك أيّ قيم فارغة لا يجب أن تكون كذلك؟
- الأحجام هل الحجم صفر عندما لا يكون كذلك؟
aside()
قد يكون من الصعب وضع كل خطوة وسيطة في التحليل في متغيّر لكي تتمكّن من طباعته وفحصه. لطباعة القيم الوسيطة من سلسلة طويلة من
عمليات استدعاء الدوال، يمكنك استخدام طريقة aside()
. على سبيل المثال:
محرِّر الرموز البرمجية (JavaScript)
var image = ee.Image(ee.ImageCollection('COPERNICUS/S2') .filterBounds(ee.Geometry.Point([-12.29, 168.83])) .aside(print) .filterDate('2011-01-01', '2016-12-31') .first());
import ee import geemap.core as geemap
Colab (Python)
image = ee.Image( ee.ImageCollection('COPERNICUS/S2_HARMONIZED') .filterBounds(ee.Geometry.Point([-12.29, 168.83])) .aside(display) .filterDate('2011-01-01', '2016-12-31') .first() )
تذكَّر أنّ aside(print)
(محرر رموز JavaScript) و
aside(display)
(Python geemap) يُطلِقان الدوالّ من جهة العميل، وسيستمرّ تعذُّر تنفيذ الدوالّ المُعرَّفة. يمكنك أيضًا استخدام aside
مع الدوالّ التي يحدّدها المستخدم. على سبيل المثال:
محرِّر الرموز البرمجية (JavaScript)
var composite = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA') .filterBounds(ee.Geometry.Point([106.91, 47.91])) .map(function(image) { return image.addBands(image.normalizedDifference(['B5', 'B4'])); }) .aside(Map.addLayer, {bands: ['B4', 'B3', 'B2'], max: 0.3}, 'collection') .qualityMosaic('nd'); Map.setCenter(106.91, 47.91, 11); Map.addLayer(composite, {bands: ['B4', 'B3', 'B2'], max: 0.3}, 'composite');
import ee import geemap.core as geemap
Colab (Python)
m = geemap.Map() m.set_center(106.91, 47.91, 11) composite = ( ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA') .filterBounds(ee.Geometry.Point([106.91, 47.91])) .map(lambda image: image.addBands(image.normalizedDifference(['B5', 'B4']))) .aside(m.add_layer, {'bands': ['B4', 'B3', 'B2'], 'max': 0.3}, 'collection') .qualityMosaic('nd') ) m.add_layer(composite, {'bands': ['B4', 'B3', 'B2'], 'max': 0.3}, 'composite') m
تشغيل دالة على first()
تكون الطباعة والعرض مفيدة في تصحيح الأخطاء عند توفّرها، ولكن عند تصحيح أخطاء دالة تمّ ربطها بمجموعة، لا يمكنك الطباعة في الدالة، كما هو موضّح في قسم الدوالّ المرتبطة. في هذه الحالة، من المفيد عزل العناصر التي تواجه مشاكل في المجموعة واختبار الدالة المُحدَّدة على عنصر فردي. عند اختبار الدالة بدون ربطها، يمكنك استخدام عبارات الطباعة لفهم المشكلة. راجِع المثال التالي.
خطأ: هذا الرمز غير صالح.
محرِّر الرموز البرمجية (JavaScript)
var image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20150821T111616_20160314T094808_T30UWU'); var someFeatures = ee.FeatureCollection([ ee.Feature(ee.Geometry.Point([-2.02, 48.43])), ee.Feature(ee.Geometry.Point([-2.80, 48.37])), ee.Feature(ee.Geometry.Point([-1.22, 48.29])), ee.Feature(ee.Geometry.Point([-1.73, 48.65])), ]); var problem = someFeatures.map(function(feature) { var dictionary = image.reduceRegion({ reducer: 'first', geometry: feature.geometry(), scale: 10, }); return feature.set({ result: ee.Number(dictionary.get('B5')) .divide(dictionary.get('B4')) }); }); // Error in map(ID=2): // Number.divide: Parameter 'left' is required. print(problem);
import ee import geemap.core as geemap
Colab (Python)
image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20150821T111616_20160314T094808_T30UWU' ) some_features = ee.FeatureCollection([ ee.Feature(ee.Geometry.Point([-2.02, 48.43])), ee.Feature(ee.Geometry.Point([-2.80, 48.37])), ee.Feature(ee.Geometry.Point([-1.22, 48.29])), ee.Feature(ee.Geometry.Point([-1.73, 48.65])), ]) # Define a function to be mapped over the collection. def function_to_map(feature): dictionary = image.reduceRegion( reducer=ee.Reducer.first(), geometry=feature.geometry(), scale=10 ) return feature.set( {'result': ee.Number(dictionary.get('B5')).divide(dictionary.get('B4'))} ) problem = some_features.map(function_to_map) # EEException: Error in map(ID=2): # Number.divide: Parameter 'left' is required. print(problem.getInfo())
لتصحيح الأخطاء، من الأفضل فحص الخطأ. لحسن الحظ، تُعلمك رسالة الخطأ هذه
بوجود مشكلة في الميزة التي تتضمّن ID=2
. لمزيد من التحقيق، من المفيد إعادة صياغة الرمز البرمجي قليلاً. على وجه التحديد، لا يمكنك
استخدام عبارات الطباعة في الدالة عند ربطها بمجموعة، كما هو موضّح في
هذا القسم. يتمثل هدف تصحيح الأخطاء في عزل الميزة التي تتضمّن مشكلة، وتشغيل الدالة
مع تضمين بعض عبارات الطباعة فيها. باستخدام الصورة والميزات نفسها المستخدَمة
سابقًا:
محرِّر الرموز البرمجية (JavaScript)
// Define a function to be mapped over the collection. var functionToMap = function(feature) { var dictionary = image.reduceRegion({ reducer: 'first', geometry: feature.geometry(), scale: 10, }); // Debug: print(dictionary); return feature.set({ result: ee.Number(dictionary.get('B5')) .divide(dictionary.get('B4')) }); }; // Isolate the feature that's creating problems. var badFeature = ee.Feature(someFeatures .filter(ee.Filter.eq('system:index', '2')) .first()); // Test the function with print statements added. functionToMap(badFeature); // Inspect the bad feature in relation to the image. Map.centerObject(badFeature, 11); Map.addLayer(badFeature, {}, 'bad feature'); Map.addLayer(image, {bands: ['B4', 'B3', 'B2'], max: 3000}, 'image');
import ee import geemap.core as geemap
Colab (Python)
# Define a function to be mapped over the collection. def function_to_map(feature): dictionary = image.reduceRegion( reducer=ee.Reducer.first(), geometry=feature.geometry(), scale=10 ) # Debug: display(dictionary) return feature.set( {'result': ee.Number(dictionary.get('B5')).divide(dictionary.get('B4'))} ) # Isolate the feature that's creating problems. bad_feature = ee.Feature( some_features.filter(ee.Filter.eq('system:index', '2')).first() ) # Test the function with print statements added. function_to_map(bad_feature) # Inspect the bad feature in relation to the image. m = geemap.Map() m.center_object(bad_feature, 11) m.add_layer(bad_feature, {}, 'bad feature') m.add_layer(image, {'bands': ['B4', 'B3', 'B2'], 'max': 3000}, 'image') m
الآن، بما أنّ الدالة لا يتم تشغيلها إلا على ميزة واحدة، يمكنك إدراج طلب طباعة (display) في ملف Python geemap. تحقَّق من العنصر المطبوع واكتشف أنّ العنصر
الذي تم إرجاعه بواسطة reduceRegion()
يحتوي على قيم فارغة لكل نطاق. يوضّح ذلك سبب تعذُّر
القسمة: لأنّه لا يمكنك قسمة القيمة الخالية على القيمة الخالية. لماذا يكون القيمة خالية في المقام الأول؟
للتحقيق في المشكلة، أضِف صورة الإدخال والعنصر الذي يتضمّن خطأ إلى الخريطة، واضبط الخريطة على العنصر الذي يتضمّن خطأ. عند إجراء ذلك، ستكتشف أنّ المشكلة ترجع إلى أنّ النقطة خارج
حدود الصورة. استنادًا إلى هذا الاكتشاف، التعليمة البرمجية التي تم تصحيح أخطاءها هي:
محرِّر الرموز البرمجية (JavaScript)
var functionToMap = function(feature) { var dictionary = image.reduceRegion({ reducer: 'first', geometry: feature.geometry(), scale: 10, }); return feature.set({ result: ee.Number(dictionary.get('B5')) .divide(dictionary.get('B4')) }); }; var noProblem = someFeatures .filterBounds(image.geometry()) .map(functionToMap); print(noProblem);
import ee import geemap.core as geemap
Colab (Python)
def function_to_map(feature): dictionary = image.reduceRegion( reducer=ee.Reducer.first(), geometry=feature.geometry(), scale=10 ) return feature.set( {'result': ee.Number(dictionary.get('B5')).divide(dictionary.get('B4'))} ) no_problem = some_features.filterBounds(image.geometry()).map(function_to_map) display(no_problem)
أداة تحليل الأداء
يقدّم أداة تحليل الأداء معلومات عن وقت استخدام وحدة التحكّم في الطاقة واستخدام الذاكرة (لكلّ خوارزمية ومادّة عرض) الناتج عن العمليات الحسابية التي يتم إجراؤها عندما تكون هذه الميزة مفعّلة. ابحث عن الإدخالات في أعلى أداة تحليل الأداء للحصول على معلومات عن العمليات الأكثر استهلاكًا للموارد. بالنسبة إلى النصوص البرمجية التي تستغرق وقتًا طويلاً أو غير الفعّالة، توفّر الإدخالات في أعلى أداة تحليل الأداء أدلة حول النقاط التي يجب التركيز عليها لتحسين النص البرمجي. ملاحظة مهمة: يؤثر أداة تحليل الأداء نفسها في أداء الرمز المبرمَج، لذا يجب تشغيلها فقط عند الضرورة.