এই ডক কোডিং অনুশীলনগুলি বর্ণনা করে যা জটিল বা ব্যয়বহুল আর্থ ইঞ্জিন কম্পিউটেশনের জন্য সাফল্যের সম্ভাবনা সর্বাধিক করার উদ্দেশ্যে। এখানে বর্ণিত পদ্ধতিগুলি ইন্টারেক্টিভ (যেমন কোড এডিটর) এবং ব্যাচ ( Export
) কম্পিউটেশন উভয়ের ক্ষেত্রেই প্রযোজ্য, যদিও ব্যাচ সিস্টেমে সাধারণত দীর্ঘ চলমান গণনা চালানো উচিত।
সার্ভার ফাংশন এবং বস্তুর সাথে ক্লায়েন্ট ফাংশন এবং অবজেক্ট মিশ্রিত করা এড়িয়ে চলুন
আর্থ ইঞ্জিন সার্ভার অবজেক্ট হল কনস্ট্রাক্টর সহ অবজেক্ট যা ee
দিয়ে শুরু হয় (যেমন ee.Image
, ee.Reducer
) এবং এই ধরনের অবজেক্টের যেকোনো পদ্ধতি হল সার্ভার ফাংশন। এই পদ্ধতিতে নির্মিত নয় এমন কোনো বস্তু একটি ক্লায়েন্ট অবজেক্ট। ক্লায়েন্ট অবজেক্টগুলি কোড এডিটর (যেমন Map
, Chart
) বা জাভাস্ক্রিপ্ট ভাষা (যেমন Date
, Math
, []
, {}
) থেকে আসতে পারে।
অনিচ্ছাকৃত আচরণ এড়াতে, এখানে এবং এখানে এবং এখানে আলোচনা করা হিসাবে আপনার স্ক্রিপ্টে ক্লায়েন্ট এবং সার্ভার ফাংশনগুলিকে মিশ্রিত করবেন না। আর্থ ইঞ্জিনে ক্লায়েন্ট বনাম সার্ভারের গভীর ব্যাখ্যার জন্য এই পৃষ্ঠা এবং/অথবা এই টিউটোরিয়ালটি দেখুন। নিম্নলিখিত উদাহরণটি ক্লায়েন্ট এবং সার্ভার কার্যকারিতা মিশ্রিত করার বিপদগুলিকে চিত্রিত করে:
ত্রুটি — এই কোড কাজ করে না!
var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Won't work.
for(var i=0; i<table.size(); i++) {
print('No!');
}
আপনি ত্রুটি চিহ্নিত করতে পারেন? মনে রাখবেন table.size()
একটি সার্ভার অবজেক্টের একটি সার্ভার পদ্ধতি এবং ক্লায়েন্ট-সাইড কার্যকারিতা যেমন <
শর্তসাপেক্ষে ব্যবহার করা যাবে না।
একটি পরিস্থিতি যেখানে আপনি UI সেটআপের জন্য লুপ ব্যবহার করতে চাইতে পারেন, যেহেতু কোড এডিটর ui
অবজেক্ট এবং পদ্ধতিগুলি ক্লায়েন্ট-সাইড। ( আর্থ ইঞ্জিনে ইউজার ইন্টারফেস তৈরি সম্পর্কে আরও জানুন )। যেমন:
UI সেটআপের জন্য ক্লায়েন্ট ফাংশন ব্যবহার করুন।
var panel = ui.Panel();
for(var i=1; i<8; i++) {
panel.widgets().set(i, ui.Button('button ' + i))
}
print(panel);
বিপরীতভাবে, map()
একটি সার্ভার ফাংশন এবং ক্লায়েন্ট কার্যকারিতা map()
এ পাস করা ফাংশনের ভিতরে কাজ করবে না। যেমন:
ত্রুটি — এই কোড কাজ করে না!
var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Error:
var foobar = table.map(function(f) {
print(f); // Can't use a client function here.
// Can't Export, either.
});
একটি সংগ্রহের প্রতিটি উপাদানের জন্য কিছু করতে, সংগ্রহের উপর একটি ফাংশন map()
এবং একটি সম্পত্তি set()
:
map()
এবং set()
ব্যবহার করুন!
var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
print(table.first());
// Do something to every element of a collection.
var withMoreProperties = table.map(function(f) {
// Set a property.
return f.set('area_sq_meters', f.area())
});
print(withMoreProperties.first());
আপনি গণনা করা বা বিদ্যমান বৈশিষ্ট্যের উপর ভিত্তি করে সংগ্রহটি filter()
করতে পারেন এবং ফলাফলটি print()
পারেন। নোট করুন যে আপনি 5000 এর বেশি উপাদান সহ একটি সংগ্রহ মুদ্রণ করতে পারবেন না। আপনি যদি "5000 টিরও বেশি উপাদান জমা করার পরে সংগ্রহের প্রশ্ন বাতিল করে" ত্রুটি পান তবে মুদ্রণের আগে সংগ্রহটি filter()
বা limit()
৷
অযথা তালিকায় রূপান্তর করা এড়িয়ে চলুন
আর্থ ইঞ্জিনে সংগ্রহগুলি অপ্টিমাইজেশান ব্যবহার করে প্রক্রিয়া করা হয় যা সংগ্রহটিকে একটি List
বা Array
টাইপে রূপান্তর করে ভাঙা হয়। আপনার সংগ্রহের উপাদানগুলিতে র্যান্ডম অ্যাক্সেসের প্রয়োজন না হলে (যেমন আপনাকে একটি সংগ্রহের i'th উপাদান পেতে হবে ), পৃথক সংগ্রহের উপাদানগুলি অ্যাক্সেস করতে সংগ্রহে ফিল্টার ব্যবহার করুন৷ নিম্নলিখিত উদাহরণটি একটি সংগ্রহে একটি উপাদান অ্যাক্সেস করতে টাইপ রূপান্তর (প্রস্তাবিত নয়) এবং ফিল্টারিং (প্রস্তাবিত) এর মধ্যে পার্থক্য ব্যাখ্যা করে:
অপ্রয়োজনীয় তালিকায় রূপান্তর করবেন না!
var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Do NOT do this!!
var list = table.toList(table.size());
print(list.get(13)); // User memory limit exceeded.
মনে রাখবেন যে আপনি অপ্রয়োজনীয়ভাবে একটি সংগ্রহকে একটি তালিকায় রূপান্তর করে সহজেই ত্রুটিগুলি ট্রিগার করতে পারেন৷ নিরাপদ উপায় হল filter()
ব্যবহার করা:
filter()
ব্যবহার করুন!
print(table.filter(ee.Filter.eq('country_na', 'Niger')).first());
মনে রাখবেন যে আপনার বিশ্লেষণে যত তাড়াতাড়ি সম্ভব ফিল্টার ব্যবহার করা উচিত।
ee.Algorithms.If()
এড়িয়ে চলুন
ব্রাঞ্চিং লজিক প্রয়োগ করতে ee.Algorithms.If()
ব্যবহার করবেন না, বিশেষ করে ম্যাপ করা ফাংশনে। নিম্নোক্ত উদাহরণটি ব্যাখ্যা করে, ee.Algorithms.If()
মেমরির নিবিড় হতে পারে এবং সুপারিশ করা হয় না:
If()
ব্যবহার করবেন না!
var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Do NOT do this!
var veryBad = table.map(function(f) {
return ee.Algorithms.If({
condition: ee.String(f.get('country_na')).compareTo('Chad').gt(0),
trueCase: f, // Do something.
falseCase: null // Do something else.
});
}, true);
print(veryBad); // User memory limit exceeded.
// If() may evaluate both the true and false cases.
মনে রাখবেন map()
এর দ্বিতীয় আর্গুমেন্ট true
। এর মানে হল ম্যাপ করা ফাংশনটি নাল রিটার্ন করতে পারে এবং সেগুলি ফলস্বরূপ সংগ্রহে ফেলে দেওয়া হবে। এটি দরকারী হতে পারে ( If()
ছাড়াই), কিন্তু এখানে সবচেয়ে সহজ সমাধান হল একটি ফিল্টার ব্যবহার করা:
filter()
ব্যবহার করুন!
print(table.filter(ee.Filter.eq('country_na', 'Chad')));
এই টিউটোরিয়ালে দেখানো হয়েছে, ফিল্টার ব্যবহার করে একটি কার্যকরী প্রোগ্রামিং পদ্ধতি হল একটি সংগ্রহের কিছু উপাদানে একটি যুক্তি এবং সংগ্রহের অন্যান্য উপাদানগুলিতে আরেকটি যুক্তি প্রয়োগ করার সঠিক উপায়।
reproject()
এড়িয়ে চলুন
একেবারে প্রয়োজনীয় না হলে পুনরায় প্রজেক্ট ব্যবহার করবেন না। আপনি reproject()
ব্যবহার করতে চাইতে পারেন এমন একটি কারণ হল কোড এডিটর গণনাগুলিকে একটি নির্দিষ্ট স্কেলে ঘটতে বাধ্য করা যাতে আপনি আপনার পছন্দসই বিশ্লেষণের স্কেলে ফলাফলগুলি পরীক্ষা করতে পারেন। পরবর্তী উদাহরণে, হট পিক্সেলের প্যাচগুলি গণনা করা হয় এবং প্রতিটি প্যাচে পিক্সেলের সংখ্যা গণনা করা হয়। উদাহরণটি চালান এবং প্যাচগুলির একটিতে ক্লিক করুন। মনে রাখবেন যে পিক্সেলের গণনা পুনঃপ্রকল্পিত ডেটার মধ্যে পৃথক হয় যে ডেটা পুনঃপ্রকল্পিত হয়নি।
var l8sr = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
var sf = ee.Geometry.Point([-122.405, 37.786]);
Map.centerObject(sf, 13);
// A reason to reproject - counting pixels and exploring interactively.
var image = l8sr.filterBounds(sf)
.filterDate('2019-06-01', '2019-12-31')
.first();
image = image.multiply(0.00341802).add(149); // Apply scale factors.
Map.addLayer(image, {bands: ['ST_B10'], min: 280, max: 317}, 'image');
var hotspots = image.select('ST_B10').gt(317)
.selfMask()
.rename('hotspots');
var objectSize = hotspots.connectedPixelCount(256);
Map.addLayer(objectSize, {min: 1, max: 256}, 'Size No Reproject', false);
// Beware of reproject! Don't zoom out on reprojected data.
var reprojected = objectSize.reproject(hotspots.projection());
Map.addLayer(reprojected, {min: 1, max: 256}, 'Size Reproject', false);
পার্থক্যের কারণ হল বিশ্লেষণের স্কেল কোড এডিটর জুম স্তর দ্বারা সেট করা হয়। reproject()
কল করে আপনি কোড এডিটরের পরিবর্তে গণনার স্কেল সেট করেন। এই ডকটিতে বর্ণিত কারণগুলির জন্য অত্যন্ত সতর্কতার সাথে reproject()
ব্যবহার করুন।
প্রথমে ফিল্টার করুন এবং select()
সাধারণভাবে, সংগ্রহের সাথে অন্য কিছু করার আগে সময়, অবস্থান এবং/অথবা মেটাডেটা দ্বারা ইনপুট সংগ্রহগুলি ফিল্টার করুন। কম নির্বাচনী ফিল্টার আগে আরো নির্বাচনী ফিল্টার প্রয়োগ করুন. স্থানিক এবং/অথবা অস্থায়ী ফিল্টারগুলি প্রায়শই বেশি নির্বাচনী হয়। উদাহরণস্বরূপ, মনে রাখবেন যে select()
এবং filter()
map()
এর আগে প্রয়োগ করা হয়েছে :
var images = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED');
var sf = ee.Geometry.Point([-122.463, 37.768]);
// Expensive function to reduce the neighborhood of an image.
var reduceFunction = function(image) {
return image.reduceNeighborhood({
reducer: ee.Reducer.mean(),
kernel: ee.Kernel.square(4)
});
};
var bands = ['B4', 'B3', 'B2'];
// Select and filter first!
var reasonableComputation = images
.select(bands)
.filterBounds(sf)
.filterDate('2018-01-01', '2019-02-01')
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 1))
.aside(print) // Useful for debugging.
.map(reduceFunction)
.reduce('mean')
.rename(bands);
var viz = {bands: bands, min: 0, max: 10000};
Map.addLayer(reasonableComputation, viz, 'reasonableComputation');
mask()
এর পরিবর্তে updateMask()
) ব্যবহার করুন
updateMask()
এবং mask()
এর মধ্যে পার্থক্য হল যে প্রাক্তনটি যুক্তি (নতুন মাস্ক) এবং বিদ্যমান ইমেজ মাস্কের একটি লজিক্যাল and()
করে যেখানে mask()
কেবল আর্গুমেন্টের সাথে ইমেজ মাস্ককে প্রতিস্থাপন করে। পরবর্তী বিপদ হল যে আপনি অনিচ্ছাকৃতভাবে পিক্সেল আনমাস্ক করতে পারেন। এই উদাহরণে, লক্ষ্য হল 300 মিটার উচ্চতার চেয়ে কম বা সমান পিক্সেল মাস্ক করা। যেমন আপনি দেখতে পাচ্ছেন (জুম আউট), mask()
ব্যবহার করার ফলে অনেক পিক্সেল মুখোশহীন হয়ে যায়, পিক্সেল যেগুলি আগ্রহের চিত্রের অন্তর্গত নয়:
var l8sr = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
var sf = ee.Geometry.Point([-122.40554461769182, 37.786807309873716]);
var aw3d30 = ee.Image('JAXA/ALOS/AW3D30_V1_1');
Map.centerObject(sf, 7);
var image = l8sr.filterBounds(sf)
.filterDate('2019-06-01', '2019-12-31')
.first();
image = image.multiply(0.0000275).subtract(0.2); // Apply scale factors.
var vis = {bands: ['SR_B4', 'SR_B3', 'SR_B2'], min: 0, max: 0.3};
Map.addLayer(image, vis, 'image', false);
var mask = aw3d30.select('AVE').gt(300);
Map.addLayer(mask, {}, 'mask', false);
// NO! Don't do this!
var badMask = image.mask(mask);
Map.addLayer(badMask, vis, 'badMask');
var goodMask = image.updateMask(mask);
Map.addLayer(goodMask, vis, 'goodMask', false);
হ্রাসকারী একত্রিত করুন
আপনার যদি একটি একক ইনপুট (যেমন একটি চিত্র অঞ্চল) থেকে একাধিক পরিসংখ্যানের (যেমন গড় এবং মানক বিচ্যুতি) প্রয়োজন হয় তবে এটি হ্রাসকারীগুলিকে একত্রিত করা আরও কার্যকর। উদাহরণস্বরূপ, চিত্রের পরিসংখ্যান পেতে, নিম্নরূপ হ্রাসকারীগুলিকে একত্রিত করুন:
var image = ee.Image(
'COPERNICUS/S2_HARMONIZED/20150821T111616_20160314T094808_T30UWU');
// Get mean and SD in every band by combining reducers.
var stats = image.reduceRegion({
reducer: ee.Reducer.mean().combine({
reducer2: ee.Reducer.stdDev(),
sharedInputs: true
}),
geometry: ee.Geometry.Rectangle([-2.15, 48.55, -1.83, 48.72]),
scale: 10,
bestEffort: true // Use maxPixels if you care about scale.
});
print(stats);
// Extract means and SDs to images.
var meansImage = stats.toImage().select('.*_mean');
var sdsImage = stats.toImage().select('.*_stdDev');
এই উদাহরণে, নোট করুন যে গড় রিডুসারকে স্ট্যান্ডার্ড ডেভিয়েশন রিডুসারের সাথে একত্রিত করা হয়েছে এবং ইনপুট পিক্সেলের মাধ্যমে একটি একক পাস সক্ষম করতে sharedInputs
সত্য। আউটপুট অভিধানে, রিডুসারের নাম ব্যান্ডের নামের সাথে যুক্ত করা হয়। গড় এবং SD চিত্রগুলি পেতে (উদাহরণস্বরূপ ইনপুট চিত্রটিকে স্বাভাবিক করার জন্য), আপনি মানগুলিকে একটি ছবিতে পরিণত করতে পারেন এবং উদাহরণে প্রদর্শিত হিসাবে পৃথকভাবে অর্থ এবং SDগুলি বের করতে রেজেক্স ব্যবহার করতে পারেন।
Export
ব্যবহার করুন
কোড এডিটরে "ব্যবহারকারীর মেমরির সীমা ছাড়িয়ে গেছে" বা "গণনার সময় শেষ" ত্রুটির ফলাফলের জন্য, একই গণনাগুলি Export
ব্যবহার করে সফল হতে পারে। কারণ ব্যাচ সিস্টেমে (যেখানে রপ্তানি চলে) চলার সময় টাইমআউট দীর্ঘ হয় এবং অনুমোদিত মেমরির পদচিহ্ন বড় হয়। ( ডিবাগিং ডক এ বিস্তারিত হিসাবে আপনি প্রথমে চেষ্টা করতে চাইতে পারেন এমন অন্যান্য পদ্ধতি রয়েছে)। আগের উদাহরণটি অব্যাহত রেখে, ধরুন যে অভিধানটি একটি ত্রুটি ফিরিয়ে দিয়েছে। আপনি কিছু করার মাধ্যমে ফলাফল পেতে পারেন:
var link = '86836482971a35a5e735a17e93c23272';
Export.table.toDrive({
collection: ee.FeatureCollection([ee.Feature(null, stats)]),
description: 'exported_stats_demo_' + link,
fileFormat: 'CSV'
});
উল্লেখ্য যে লিঙ্কটি পুনরুত্পাদনযোগ্যতার জন্য সম্পদের নামের সাথে এমবেড করা হয়েছে। এছাড়াও মনে রাখবেন যে আপনি যদি toAsset
রপ্তানি করতে চান তবে আপনাকে একটি জ্যামিতি সরবরাহ করতে হবে, যা যেকোনও হতে পারে, উদাহরণস্বরূপ চিত্র সেন্ট্রোয়েড, যা ছোট এবং গণনা করা সস্তা। (যদি আপনার এটির প্রয়োজন না হয় তবে একটি জটিল জ্যামিতি ব্যবহার করবেন না)।
কম্পিউটেশন টাইম আউট এবং অনেক সমসাময়িক সমষ্টি সমাধান করতে Export
ব্যবহারের উদাহরণগুলির জন্য ডিবাগিং পৃষ্ঠাটি দেখুন। সাধারণভাবে রপ্তানি সম্পর্কে বিশদ বিবরণের জন্য এই নথিটি দেখুন।
আপনার যদি ক্লিপ করার প্রয়োজন না হয় তবে clip()
ব্যবহার করবেন না
clip()
অপ্রয়োজনীয়ভাবে ব্যবহার করলে গণনার সময় বৃদ্ধি পাবে । আপনার বিশ্লেষণের জন্য প্রয়োজনীয় না হলে clip()
এড়িয়ে চলুন। আপনি যদি নিশ্চিত না হন তবে ক্লিপ করবেন না। ক্লিপের খারাপ ব্যবহারের একটি উদাহরণ:
অপ্রয়োজনীয়ভাবে ইনপুট ক্লিপ করবেন না!
var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
var l8sr = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
var belgium = table.filter(ee.Filter.eq('country_na', 'Belgium')).first();
// Do NOT clip unless you need to.
var unnecessaryClip = l8sr
.select('SR_B4') // Good.
.filterBounds(belgium.geometry()) // Good.
.filterDate('2019-01-01', '2019-04-01') // Good.
.map(function(image) {
return image.clip(belgium.geometry()); // NO! Bad! Not necessary.
})
.median()
.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: belgium.geometry(),
scale: 30,
maxPixels: 1e10,
});
print(unnecessaryClip);
ইনপুট ইমেজ ক্লিপ করা সম্পূর্ণভাবে বাদ দেওয়া যেতে পারে, কারণ reduceRegion()
কলে অঞ্চলটি নির্দিষ্ট করা আছে:
আউটপুটের জন্য অঞ্চল নির্দিষ্ট করুন!
var noClipNeeded = l8sr
.select('SR_B4') // Good.
.filterBounds(belgium.geometry()) // Good.
.filterDate('2019-01-01', '2019-12-31') // Good.
.median()
.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: belgium.geometry(), // Geometry is specified here.
scale: 30,
maxPixels: 1e10,
});
print(noClipNeeded);
এই গণনার সময় শেষ হলে, এই উদাহরণের মতো এটি Export
।
আপনি যদি একটি জটিল সংগ্রহের সাথে ক্লিপ করতে চান তবে clipToCollection()
ব্যবহার করুন
আপনার যদি সত্যিই কিছু ক্লিপ করার প্রয়োজন হয় এবং আপনি ক্লিপিংয়ের জন্য যে জ্যামিতিগুলি ব্যবহার করতে চান তা সংগ্রহে থাকে, clipToCollection()
ব্যবহার করুন :
var ecoregions = ee.FeatureCollection('RESOLVE/ECOREGIONS/2017');
var image = ee.Image('JAXA/ALOS/AW3D30_V1_1');
var complexCollection = ecoregions
.filter(ee.Filter.eq('BIOME_NAME',
'Tropical & Subtropical Moist Broadleaf Forests'));
Map.addLayer(complexCollection, {}, 'complexCollection');
var clippedTheRightWay = image.select('AVE')
.clipToCollection(complexCollection);
Map.addLayer(clippedTheRightWay, {}, 'clippedTheRightWay', false);
বড় এবং/অথবা জটিল সংগ্রহগুলিতে featureCollection.geometry()
বা featureCollection.union()
ব্যবহার করবেন না, যা আরও বেশি মেমরির নিবিড় হতে পারে।
একটি হ্রাসকারীর জন্য অঞ্চল হিসাবে একটি জটিল সংগ্রহ ব্যবহার করবেন না
যদি আপনি একটি স্থানিক হ্রাস করতে চান যাতে রিডুসার একাধিক অঞ্চল থেকে একটি FeatureCollection
এ ইনপুট পুল করে, তাহলে রিডুসারে geometry
ইনপুট হিসাবে featureCollection.geometry()
সরবরাহ করবেন না। পরিবর্তে, clipToCollection()
ব্যবহার করুন এবং সংগ্রহের সীমানা জুড়ে যথেষ্ট বড় একটি অঞ্চল। যেমন:
var ecoregions = ee.FeatureCollection('RESOLVE/ECOREGIONS/2017');
var image = ee.Image('JAXA/ALOS/AW3D30_V1_1');
var complexCollection = ecoregions
.filter(ee.Filter.eq('BIOME_NAME', 'Tropical & Subtropical Moist Broadleaf Forests'));
var clippedTheRightWay = image.select('AVE')
.clipToCollection(complexCollection);
Map.addLayer(clippedTheRightWay, {}, 'clippedTheRightWay');
var reduction = clippedTheRightWay.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: ee.Geometry.Rectangle({
coords: [-179.9, -50, 179.9, 50], // Almost global.
geodesic: false
}),
scale: 30,
maxPixels: 1e12
});
print(reduction); // If this times out, export it.
একটি অ-শূন্য errorMargin
ব্যবহার করুন
সম্ভাব্য ব্যয়বহুল জ্যামিতি ক্রিয়াকলাপের জন্য, গণনার প্রয়োজনীয় নির্ভুলতা দেওয়া সম্ভব সবচেয়ে বড় ত্রুটি মার্জিন ব্যবহার করুন। ত্রুটি মার্জিন জ্যামিতিতে অপারেশনের সময় অনুমোদিত সর্বাধিক অনুমোদিত ত্রুটি (মিটারে) নির্দিষ্ট করে (যেমন পুনরুজ্জীবনের সময়)। একটি ছোট ত্রুটি মার্জিন নির্দিষ্ট করার ফলে জ্যামিতি (স্থানাঙ্ক সহ) ঘনীভূত করার প্রয়োজন হতে পারে, যা মেমরি নিবিড় হতে পারে। আপনার গণনার জন্য যতটা সম্ভব বড় একটি ত্রুটি মার্জিন নির্দিষ্ট করা ভাল অনুশীলন:
var ecoregions = ee.FeatureCollection('RESOLVE/ECOREGIONS/2017');
var complexCollection = ecoregions.limit(10);
Map.centerObject(complexCollection);
Map.addLayer(complexCollection);
var expensiveOps = complexCollection.map(function(f) {
return f.buffer(10000, 200).bounds(200);
});
Map.addLayer(expensiveOps, {}, 'expensiveOps');
reduceToVectors()
এর সাথে হাস্যকরভাবে ছোট স্কেল ব্যবহার করবেন না
আপনি যদি একটি রাস্টারকে একটি ভেক্টরে রূপান্তর করতে চান তবে একটি উপযুক্ত স্কেল ব্যবহার করুন। একটি খুব ছোট স্কেল নির্দিষ্ট করা গণনার খরচ উল্লেখযোগ্যভাবে বৃদ্ধি করতে পারে। যতটা সম্ভব উচ্চ স্কেল সেট করুন প্রয়োজনীয় নির্ভুলতা দিন। উদাহরণস্বরূপ, বিশ্বব্যাপী ভূমি জনগণের প্রতিনিধিত্বকারী বহুভুজ পেতে:
var etopo = ee.Image('NOAA/NGDC/ETOPO1');
// Approximate land boundary.
var bounds = etopo.select(0).gt(-100);
// Non-geodesic polygon.
var almostGlobal = ee.Geometry.Polygon({
coords: [[-180, -80], [180, -80], [180, 80], [-180, 80], [-180, -80]],
geodesic: false
});
Map.addLayer(almostGlobal, {}, 'almostGlobal');
var vectors = bounds.selfMask().reduceToVectors({
reducer: ee.Reducer.countEvery(),
geometry: almostGlobal,
// Set the scale to the maximum possible given
// the required precision of the computation.
scale: 50000,
});
Map.addLayer(vectors, {}, 'vectors');
আগের উদাহরণে, বৈশ্বিক হ্রাসে ব্যবহারের জন্য একটি নন-জিওডেসিক বহুভুজের ব্যবহার লক্ষ্য করুন।
reduceRegions reduceToVectors()
reduceRegions()
ব্যবহার করবেন না
reduceRegions()
জন্য ইনপুট হিসাবে reduceToVectors()
দ্বারা প্রত্যাবর্তিত একটি FeatureCollection
ব্যবহার করবেন না। পরিবর্তে, reduceToVectors()
কল করার আগে আপনি যে ব্যান্ডগুলি কমাতে চান তা যোগ করুন :
var etopo = ee.Image('NOAA/NGDC/ETOPO1');
var mod11a1 = ee.ImageCollection('MODIS/006/MOD11A1');
// Approximate land boundary.
var bounds = etopo.select(0).gt(-100);
// Non-geodesic polygon.
var almostGlobal = ee.Geometry.Polygon({
coords: [[-180, -80], [180, -80], [180, 80], [-180, 80], [-180, -80]],
geodesic: false
});
var lst = mod11a1.first().select(0);
var means = bounds.selfMask().addBands(lst).reduceToVectors({
reducer: ee.Reducer.mean(),
geometry: almostGlobal,
scale: 1000,
maxPixels: 1e10
});
print(means.limit(10));
মনে রাখবেন যে একটি চিত্রের পিক্সেলকে অন্য অঞ্চলের মধ্যে হ্রাস করার অন্যান্য উপায়গুলির মধ্যে রয়েছে reduceConnectedComponents() এবং/অথবা গ্রুপিং রিডুসার ।
পাড়ার ক্রিয়াকলাপের জন্য fastDistanceTransform()
ব্যবহার করুন
কিছু কনভল্যুশন অপারেশনের জন্য, fastDistanceTransform()
reduceNeighborhood()
বা convolve()
এর চেয়ে বেশি দক্ষ হতে পারে। উদাহরণস্বরূপ, বাইনারি ইনপুটগুলির ক্ষয় এবং/অথবা প্রসারণ করতে:
var aw3d30 = ee.Image('JAXA/ALOS/AW3D30_V1_1');
// Make a simple binary layer from a threshold on elevation.
var mask = aw3d30.select('AVE').gt(300);
Map.setCenter(-122.0703, 37.3872, 11);
Map.addLayer(mask, {}, 'mask');
// Distance in pixel units.
var distance = mask.fastDistanceTransform().sqrt();
// Threshold on distance (three pixels) for a dilation.
var dilation = distance.lt(3);
Map.addLayer(dilation, {}, 'dilation');
// Do the reverse for an erosion.
var notDistance = mask.not().fastDistanceTransform().sqrt();
var erosion = notDistance.gt(3);
Map.addLayer(erosion, {}, 'erosion');
reduceNeighborhood()
এ অপ্টিমাইজেশন ব্যবহার করুন
আপনি যদি একটি কনভল্যুশন সঞ্চালন করতে চান এবং fastDistanceTransform()
ব্যবহার করতে না পারেন, তাহলে reduceNeighborhood()
এ অপ্টিমাইজেশন ব্যবহার করুন।
var l8raw = ee.ImageCollection('LANDSAT/LC08/C02/T1_RT');
var composite = ee.Algorithms.Landsat.simpleComposite(l8raw);
var bands = ['B4', 'B3', 'B2'];
var optimizedConvolution = composite.select(bands).reduceNeighborhood({
reducer: ee.Reducer.mean(),
kernel: ee.Kernel.square(3),
optimization: 'boxcar' // Suitable optimization for mean.
}).rename(bands);
var viz = {bands: bands, min: 0, max: 72};
Map.setCenter(-122.0703, 37.3872, 11);
Map.addLayer(composite, viz, 'composite');
Map.addLayer(optimizedConvolution, viz, 'optimizedConvolution');
আপনার প্রয়োজনের চেয়ে বেশি ডেটা নমুনা করবেন না
আপনার প্রশিক্ষণ ডেটাসেটের আকার অপ্রয়োজনীয়ভাবে বাড়ানোর তাগিদকে প্রতিহত করুন। যদিও কিছু পরিস্থিতিতে প্রশিক্ষণের ডেটার পরিমাণ বাড়ানো একটি কার্যকর মেশিন লার্নিং কৌশল, তবে এটি নির্ভুলতার কোনও অনুরূপ বৃদ্ধি ছাড়াই গণনামূলক খরচ বাড়াতে পারে। (প্রশিক্ষণ ডেটাসেটের আকার কখন বাড়াতে হবে তা বোঝার জন্য, এই রেফারেন্সটি দেখুন)। নিম্নোক্ত উদাহরণটি দেখায় যে কীভাবে অত্যধিক প্রশিক্ষণ ডেটার অনুরোধ করা ভয়ঙ্কর "গণনা করা মান খুব বড়" ত্রুটির কারণ হতে পারে:
খুব বেশি ডেটা নমুনা করবেন না!
var l8raw = ee.ImageCollection('LANDSAT/LC08/C02/T1_RT');
var composite = ee.Algorithms.Landsat.simpleComposite(l8raw);
var labels = ee.FeatureCollection('projects/google/demo_landcover_labels');
// No! Not necessary. Don't do this:
labels = labels.map(function(f) { return f.buffer(100000, 1000); });
var bands = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7'];
var training = composite.select(bands).sampleRegions({
collection: labels,
properties: ['landcover'],
scale: 30
});
var classifier = ee.Classifier.smileCart().train({
features: training,
classProperty: 'landcover',
inputProperties: bands
});
print(classifier.explain()); // Computed value is too large
আপনি আপনার পছন্দসই নির্ভুলতা অর্জন করতে পারেন কিনা তা নির্ধারণ করতে একটি মাঝারি পরিমাণ ডেটা দিয়ে শুরু করা এবং ক্লাসিফায়ারের হাইপারপ্যারামিটারগুলি টিউন করা আরও ভাল পদ্ধতি:
টিউন হাইপারপ্যারামিটার!
var l8raw = ee.ImageCollection('LANDSAT/LC08/C02/T1_RT');
var composite = ee.Algorithms.Landsat.simpleComposite(l8raw);
var labels = ee.FeatureCollection('projects/google/demo_landcover_labels');
// Increase the data a little bit, possibly introducing noise.
labels = labels.map(function(f) { return f.buffer(100, 10); });
var bands = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7'];
var data = composite.select(bands).sampleRegions({
collection: labels,
properties: ['landcover'],
scale: 30
});
// Add a column of uniform random numbers called 'random'.
data = data.randomColumn();
// Partition into training and testing.
var training = data.filter(ee.Filter.lt('random', 0.5));
var testing = data.filter(ee.Filter.gte('random', 0.5));
// Tune the minLeafPopulation parameter.
var minLeafPops = ee.List.sequence(1, 10);
var accuracies = minLeafPops.map(function(p) {
var classifier = ee.Classifier.smileCart({minLeafPopulation: p})
.train({
features: training,
classProperty: 'landcover',
inputProperties: bands
});
return testing
.classify(classifier)
.errorMatrix('landcover', 'classification')
.accuracy();
});
print(ui.Chart.array.values({
array: ee.Array(accuracies),
axis: 0,
xLabels: minLeafPops
}));
এই উদাহরণে, ক্লাসিফায়ারটি ইতিমধ্যেই খুব নির্ভুল, তাই খুব বেশি টিউনিং করতে হবে না। আপনি সম্ভবত সবচেয়ে ছোট গাছটি বেছে নিতে চাইতে পারেন (যেমন বৃহত্তম minLeafPopulation
) যার এখনও প্রয়োজনীয় নির্ভুলতা রয়েছে।
মধ্যবর্তী ফলাফল Export
ধরুন আপনার উদ্দেশ্য একটি অপেক্ষাকৃত জটিল কম্পিউটেড ইমেজ থেকে নমুনা নেওয়া। এটি প্রায়শই toAsset()
চিত্রটি Export
, রপ্তানি করা চিত্র লোড করা, তারপর নমুনা করা আরও দক্ষ। যেমন:
var image = ee.Image('UMD/hansen/global_forest_change_2018_v1_6');
var geometry = ee.Geometry.Polygon(
[[[-76.64069800085349, 5.511777325802095],
[-76.64069800085349, -20.483938229362376],
[-35.15632300085349, -20.483938229362376],
[-35.15632300085349, 5.511777325802095]]], null, false);
var testRegion = ee.Geometry.Polygon(
[[[-48.86726050085349, -3.0475996402515717],
[-48.86726050085349, -3.9248707849303295],
[-47.46101050085349, -3.9248707849303295],
[-47.46101050085349, -3.0475996402515717]]], null, false);
// Forest loss in 2016, to stratify a sample.
var loss = image.select('lossyear');
var loss16 = loss.eq(16).rename('loss16');
// Scales and masks Landsat 8 surface reflectance images.
function prepSrL8(image) {
var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
return image.addBands(opticalBands, null, true)
.addBands(thermalBands, null, true)
.updateMask(qaMask);
}
var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
.map(prepSrL8);
// Create two annual cloud-free composites.
var composite1 = collection.filterDate('2015-01-01', '2015-12-31').median();
var composite2 = collection.filterDate('2017-01-01', '2017-12-31').median();
// We want a strtatified sample of this stack.
var stack = composite1.addBands(composite2)
.float(); // Export the smallest size possible.
// Export the image. This block is commented because the export is complete.
/*
var link = '0b8023b0af6c1b0ac7b5be649b54db06'
var desc = 'Logistic_regression_stack_' + link;
Export.image.toAsset({
image: stack,
description: desc,
assetId: desc,
region: geometry,
scale: 30,
maxPixels: 1e10
})
*/
// Load the exported image.
var exportedStack = ee.Image(
'projects/google/Logistic_regression_stack_0b8023b0af6c1b0ac7b5be649b54db06');
// Take a very small sample first, to debug.
var testSample = exportedStack.addBands(loss16).stratifiedSample({
numPoints: 1,
classBand: 'loss16',
region: testRegion,
scale: 30,
geometries: true
});
print(testSample); // Check this in the console.
// Take a large sample.
var sample = exportedStack.addBands(loss16).stratifiedSample({
numPoints: 10000,
classBand: 'loss16',
region: geometry,
scale: 30,
});
// Export the large sample...
এই উদাহরণে, মনে রাখবেন যে চিত্রগুলি ফ্লোট হিসাবে রপ্তানি করা হয়েছে৷ একেবারে প্রয়োজনীয় না হলে দ্বিগুণ নির্ভুলতায় রপ্তানি করবেন না। এই রপ্তানি করার সময়, নোট করুন যে একটি কোড এডিটর লিঙ্ক (রপ্তানির আগে অবিলম্বে প্রাপ্ত) পুনরুত্পাদনযোগ্যতার জন্য ফাইলের নামটিতে এমবেড করা হয়েছে৷
একবার রপ্তানি সম্পন্ন হলে, সম্পদটি পুনরায় লোড করুন এবং এটি থেকে নমুনা নিয়ে এগিয়ে যান। নোট করুন যে একটি খুব ছোট পরীক্ষার এলাকায় একটি খুব ছোট নমুনা ডিবাগিংয়ের জন্য প্রথমে চালানো হয়। যখন এটি সফল দেখানো হয়, একটি বড় নমুনা নিন এবং এটি রপ্তানি করুন। এই ধরনের বড় নমুনা সাধারণত রপ্তানি করা প্রয়োজন. এই জাতীয় নমুনাগুলি প্রথমে রপ্তানি না করেই ইন্টারেক্টিভভাবে (উদাহরণস্বরূপ print()
) বা ব্যবহারযোগ্য (উদাহরণস্বরূপ একটি শ্রেণিবদ্ধকারীতে ইনপুট হিসাবে) উপলব্ধ হওয়ার আশা করবেন না।
যোগদান বনাম মানচিত্র-ফিল্টার
ধরুন আপনি সময়, অবস্থান বা কিছু মেটাডেটা সম্পত্তির উপর ভিত্তি করে সংগ্রহে যোগ দিতে চান। সাধারণত, এটি একটি যোগদানের মাধ্যমে সবচেয়ে দক্ষতার সাথে সম্পন্ন করা হয়। নিম্নলিখিত উদাহরণটি ল্যান্ডস্যাট 8 এবং সেন্টিনেল -2 সংগ্রহের মধ্যে একটি স্থানিক-অস্থায়ী যোগদান করে:
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED')
.filterBounds(ee.Geometry.Point([-2.0205, 48.647]));
var l8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
var joined = ee.Join.saveAll('landsat').apply({
primary: s2,
secondary: l8,
condition: ee.Filter.and(
ee.Filter.maxDifference({
difference: 1000 * 60 * 60 * 24, // One day in milliseconds
leftField: 'system:time_start',
rightField: 'system:time_start',
}),
ee.Filter.intersects({
leftField: '.geo',
rightField: '.geo',
})
)
});
print(joined);
যদিও আপনার প্রথমে জয়েন করার চেষ্টা করা উচিত (প্রয়োজন হলে Export
), মাঝে মাঝে একটি map()
মধ্যে একটি filter()
ও কার্যকর হতে পারে, বিশেষ করে খুব বড় সংগ্রহের জন্য।
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED')
.filterBounds(ee.Geometry.Point([-2.0205, 48.647]));
var l8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
var mappedFilter = s2.map(function(image) {
var date = image.date();
var landsat = l8
.filterBounds(image.geometry())
.filterDate(date.advance(-1, 'day'), date.advance(1, 'day'));
// Return the input image with matching scenes in a property.
return image.set({
landsat: landsat,
size: landsat.size()
});
}).filter(ee.Filter.gt('size', 0));
print(mappedFilter);
reduceRegion()
বনাম reduceRegions()
বনাম লুপের জন্য
ইনপুট হিসাবে একটি খুব বড় বা জটিল FeatureCollection
সাথে reduceRegions()
কল করার ফলে ভয়ঙ্কর "গণনা করা মান খুব বড়" ত্রুটি হতে পারে। একটি সম্ভাব্য সমাধান হল এর পরিবর্তে FeatureCollection
উপরে reduceRegion()
ম্যাপ করা। আরেকটি সম্ভাব্য সমাধান হল একটি (হাঁপা) ফর-লুপ ব্যবহার করা। যদিও এখানে বর্ণিত আর্থ ইঞ্জিনে এটিকে দৃঢ়ভাবে নিরুৎসাহিত করা হয়েছে, এখানে এবং এখানে , বৃহৎ হ্রাস সম্পাদন করার জন্য একটি ফর-লুপে reduceRegion()
প্রয়োগ করা যেতে পারে।
ধরুন আপনার উদ্দেশ্য হল একটি ImageCollection
এ প্রতিটি ছবির জন্য FeatureCollection
প্রতিটি ফিচারে পিক্সেলের গড় (বা কোনো পরিসংখ্যান) পাওয়া। নিম্নলিখিত উদাহরণটি পূর্বে বর্ণিত তিনটি পদ্ধতির তুলনা করে:
// Table of countries.
var countriesTable = ee.FeatureCollection("USDOS/LSIB_SIMPLE/2017");
// Time series of images.
var mod13a1 = ee.ImageCollection("MODIS/006/MOD13A1");
// MODIS vegetation indices (always use the most recent version).
var band = 'NDVI';
var imagery = mod13a1.select(band);
// Option 1: reduceRegions()
var testTable = countriesTable.limit(1); // Do this outside map()s and loops.
var data = imagery.map(function(image) {
return image.reduceRegions({
collection: testTable,
reducer: ee.Reducer.mean(),
scale: 500
}).map(function(f) {
return f.set({
time: image.date().millis(),
date: image.date().format()
});
});
}).flatten();
print(data.first());
// Option 2: mapped reduceRegion()
var data = countriesTable.map(function(feature) {
return imagery.map(function(image) {
return ee.Feature(feature.geometry().centroid(100),
image.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: feature.geometry(),
scale: 500
})).set({
time: image.date().millis(),
date: image.date().format()
}).copyProperties(feature);
});
}).flatten();
print(data.first());
// Option 3: for-loop (WATCH OUT!)
var size = countriesTable.size();
// print(size); // 312
var countriesList = countriesTable.toList(1); // Adjust size.
var data = ee.FeatureCollection([]); // Empty table.
for (var j=0; j<1; j++) { // Adjust size.
var feature = ee.Feature(countriesList.get(j));
// Convert ImageCollection > FeatureCollection
var fc = ee.FeatureCollection(imagery.map(function(image) {
return ee.Feature(feature.geometry().centroid(100),
image.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: feature.geometry(),
scale: 500
})).set({
time: image.date().millis(),
date: image.date().format()
}).copyProperties(feature);
}));
data = data.merge(fc);
}
print(data.first());
নোট করুন যে প্রতিটি সংগ্রহ থেকে first()
জিনিসটি মুদ্রিত হয়, ডিবাগিংয়ের উদ্দেশ্যে। আপনার আশা করা উচিত নয় যে সম্পূর্ণ ফলাফল ইন্টারেক্টিভভাবে উপলব্ধ হবে: আপনাকে Export
করতে হবে। এছাড়াও নোট করুন যে ফর-লুপগুলি অত্যন্ত সতর্কতার সাথে এবং শুধুমাত্র একটি শেষ অবলম্বন হিসাবে ব্যবহার করা উচিত। অবশেষে, ফর-লুপের জন্য ম্যানুয়ালি ইনপুট সংগ্রহের আকার প্রাপ্ত করা এবং উপযুক্ত স্থানে হার্ডকোডিং করা প্রয়োজন। যদি এর কোনটি আপনার কাছে অস্পষ্ট মনে হয় তবে একটি ফর-লুপ ব্যবহার করবেন না।
সময়ে প্রতিবেশীদের জন্য ফরওয়ার্ড ডিফারেন্সিং ব্যবহার করুন
ধরুন আপনার কাছে একটি সাময়িকভাবে সাজানো ImageCollection
(যেমন একটি টাইম সিরিজ) আছে এবং আপনি প্রতিটি ছবিকে আগের (বা পরবর্তী) ছবির সাথে তুলনা করতে চান। এই উদ্দেশ্যে iterate()
ব্যবহার করার পরিবর্তে, একটি অ্যারে-ভিত্তিক ফরোয়ার্ড ডিফারেন্সিং ব্যবহার করা আরও কার্যকর হতে পারে। নিম্নলিখিত উদাহরণটি সেন্টিনেল-2 সংগ্রহকে ডি-ডুপ্লিকেট করতে এই পদ্ধতি ব্যবহার করে, যেখানে সদৃশগুলিকে বছরের একই দিনের ছবি হিসাবে সংজ্ঞায়িত করা হয়:
var sentinel2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
var sf = ee.Geometry.Point([-122.47555371521855, 37.76884708376152]);
var s2 = sentinel2
.filterBounds(sf)
.filterDate('2018-01-01', '2019-12-31');
var withDoys = s2.map(function(image) {
var ndvi = image.normalizedDifference(['B4', 'B8']).rename('ndvi');
var date = image.date();
var doy = date.getRelative('day', 'year');
var time = image.metadata('system:time_start');
var doyImage = ee.Image(doy)
.rename('doy')
.int();
return ndvi.addBands(doyImage).addBands(time)
.clip(image.geometry()); // Appropriate use of clip.
});
var array = withDoys.toArray();
var timeAxis = 0;
var bandAxis = 1;
var dedupe = function(array) {
var time = array.arraySlice(bandAxis, -1);
var sorted = array.arraySort(time);
var doy = sorted.arraySlice(bandAxis, -2, -1);
var left = doy.arraySlice(timeAxis, 1);
var right = doy.arraySlice(timeAxis, 0, -1);
var mask = ee.Image(ee.Array([[1]]))
.arrayCat(left.neq(right), timeAxis);
return array.arrayMask(mask);
};
var deduped = dedupe(array);
// Inspect these outputs to confirm that duplicates have been removed.
print(array.reduceRegion('first', sf, 10));
print(deduped.reduceRegion('first', sf, 10));
সদৃশগুলি সরানো হয়েছে তা যাচাই করতে মুদ্রিত সংগ্রহগুলি পরিদর্শন করুন৷