Dokumen ini menjelaskan praktik coding yang dimaksudkan untuk memaksimalkan peluang keberhasilan komputasi Earth Engine yang kompleks atau mahal. Metode
yang dijelaskan di sini berlaku untuk komputasi interaktif (misalnya, Code Editor) dan batch
(Export
), meskipun umumnya komputasi yang berjalan lama harus
berjalan di sistem batch.
Hindari mencampur fungsi dan objek klien dengan fungsi dan objek server
Objek server Earth Engine adalah objek dengan konstruktor yang dimulai dengan ee
(misalnya, ee.Image
, ee.Reducer
) dan metode apa pun pada objek tersebut adalah fungsi
server. Setiap objek yang tidak dibuat dengan cara ini adalah objek klien. Objek
klien dapat berasal dari Editor Kode (misalnya, Map
, Chart
) atau bahasa
JavaScript (misalnya, Date
, Math
, []
, {}
).
Untuk menghindari perilaku yang tidak diinginkan, jangan gabungkan fungsi klien dan server dalam skrip Anda seperti yang dibahas di sini dan di sini serta di sini. Lihat halaman ini dan/atau tutorial ini untuk penjelasan mendalam tentang klien vs. server di Earth Engine. Contoh berikut menggambarkan bahaya mencampur fungsi klien dan server:
Error — kode ini tidak berfungsi.
var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Won't work.
for(var i=0; i<table.size(); i++) {
print('No!');
}
Dapatkah Anda menemukan error-nya? Perhatikan bahwa table.size()
adalah metode server pada objek
server dan tidak dapat digunakan dengan fungsi sisi klien seperti kondisional
<
.
Situasi yang mungkin Anda inginkan untuk menggunakan loop for adalah dengan penyiapan UI, karena objek dan metode ui
Editor
Kode bersifat sisi klien. (Pelajari lebih lanjut cara membuat antarmuka pengguna di Earth Engine). Contoh:
Gunakan fungsi klien untuk penyiapan UI.
var panel = ui.Panel();
for(var i=1; i<8; i++) {
panel.widgets().set(i, ui.Button('button ' + i))
}
print(panel);
Sebaliknya, map()
adalah fungsi server dan fungsi klien tidak akan berfungsi
di dalam fungsi yang diteruskan ke map()
. Contoh:
Error — kode ini tidak berfungsi.
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.
});
Untuk melakukan sesuatu pada setiap elemen dalam koleksi, map()
fungsi di atas
koleksi dan set()
properti:
Gunakan map()
dan 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());
Anda juga dapat filter()
koleksi berdasarkan properti yang dihitung atau yang ada, dan print()
hasilnya. Perhatikan bahwa Anda tidak dapat mencetak koleksi dengan lebih dari
5.000 elemen. Jika Anda mendapatkan error "Kueri koleksi dibatalkan setelah mengumpulkan lebih dari
5.000 elemen", filter()
atau limit()
koleksi sebelum dicetak.
Menghindari konversi ke daftar yang tidak perlu
Koleksi di Earth Engine diproses menggunakan pengoptimalan yang dipecah dengan mengonversi koleksi ke jenis List
atau Array
. Kecuali jika Anda memerlukan akses
acak ke elemen koleksi (yaitu Anda memerlukan elemen ke-i dari
koleksi), gunakan filter pada koleksi untuk mengakses setiap elemen
koleksi. Contoh berikut mengilustrasikan perbedaan antara konversi
jenis (tidak direkomendasikan) dan pemfilteran (direkomendasikan) untuk mengakses elemen dalam
koleksi:
Jangan konversi ke daftar jika tidak perlu.
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.
Perhatikan bahwa Anda dapat dengan mudah memicu error dengan mengonversi koleksi menjadi daftar
tanpa perlu. Cara yang lebih aman adalah menggunakan filter()
:
Gunakan filter()
.
print(table.filter(ee.Filter.eq('country_na', 'Niger')).first());
Perhatikan bahwa Anda harus menggunakan filter sedini mungkin dalam analisis.
Menghindarinee.Algorithms.If()
Jangan gunakan ee.Algorithms.If()
untuk menerapkan logika percabangan, terutama dalam
fungsi yang dipetakan. Seperti yang diilustrasikan contoh berikut, ee.Algorithms.If()
dapat
menggunakan banyak memori dan tidak direkomendasikan:
Jangan gunakan 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.
Perhatikan bahwa argumen kedua untuk map()
adalah true
. Artinya, fungsi yang dipetakan
dapat menampilkan null dan akan dihapus dalam koleksi yang dihasilkan.
Hal ini dapat berguna (tanpa If()
), tetapi di sini solusi termudah adalah menggunakan
filter:
Gunakan filter()
.
print(table.filter(ee.Filter.eq('country_na', 'Chad')));
Seperti yang ditunjukkan dalam tutorial ini, pendekatan pemrograman fungsional menggunakan filter adalah cara yang benar untuk menerapkan satu logika ke beberapa elemen koleksi dan logika lain ke elemen koleksi lainnya.
Menghindarinreproject()
Jangan gunakan proyeksi ulang kecuali jika benar-benar diperlukan. Salah satu alasan Anda mungkin ingin
menggunakan reproject()
adalah untuk memaksa komputasi Code Editor terjadi pada skala
tertentu sehingga Anda dapat memeriksa hasilnya pada skala analisis yang diinginkan. Dalam
contoh berikutnya, patch piksel panas dihitung dan jumlah piksel di setiap
patch dihitung. Jalankan contoh dan klik salah satu patch. Perhatikan bahwa jumlah piksel berbeda antara data yang diproyeksikan ulang dan data yang belum diproyeksikan ulang.
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);
Penyebab perbedaan ini karena skala
analisis ditetapkan oleh tingkat zoom Code Editor. Dengan
memanggil reproject()
, Anda menetapkan skala komputasi, bukan Editor
Kode. Gunakan reproject()
dengan sangat hati-hati karena alasan yang dijelaskan dalam dokumen
ini.
Filter dan select()
terlebih dahulu
Secara umum, filter koleksi input berdasarkan waktu, lokasi, dan/atau metadata sebelum
melakukan hal lain dengan koleksi. Terapkan filter yang lebih selektif sebelum
filter yang kurang selektif. Filter spasial dan/atau temporal sering kali lebih
selektif. Misalnya, perhatikan bahwa select()
dan filter()
diterapkan sebelum
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');
Gunakan updateMask()
, bukan mask()
Perbedaan antara updateMask()
dan mask()
adalah yang pertama melakukan
and()
logis dari argumen (mask baru) dan mask gambar yang ada
sedangkan mask()
hanya mengganti mask gambar dengan argumen. Bahaya dari
hal kedua adalah Anda dapat secara tidak sengaja mengungkap piksel. Dalam contoh ini, tujuannya adalah untuk menyamarkan piksel dengan ketinggian kurang dari atau sama dengan 300 meter. Seperti yang dapat
Anda lihat (perkecil), penggunaan mask()
menyebabkan banyak piksel menjadi tidak disamarkan, piksel
yang tidak termasuk dalam gambar yang diinginkan:
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);
Menggabungkan pengurangan
Jika Anda memerlukan beberapa statistik (misalnya, rata-rata dan deviasi standar) dari satu input (misalnya, wilayah gambar), akan lebih efisien untuk menggabungkan pengurangan. Misalnya, untuk mendapatkan statistik gambar, gabungkan pengurangan sebagai berikut:
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');
Dalam contoh ini, perhatikan bahwa pengurangan rata-rata digabungkan dengan pengurangan deviasi
standar dan sharedInputs
bernilai benar untuk mengaktifkan satu penerusan melalui
piksel input. Dalam kamus output, nama reducer ditambahkan ke
nama band. Untuk mendapatkan gambar mean dan SD (misalnya untuk menormalisasi gambar
input), Anda dapat mengubah nilai menjadi gambar dan menggunakan ekspresi reguler untuk mengekstrak mean
dan SD satu per satu seperti yang ditunjukkan dalam contoh.
Gunakan Export
Untuk komputasi yang menghasilkan error "Batas memori pengguna terlampaui" atau "Waktu tunggu komputasi habis" di Code Editor, komputasi yang sama mungkin dapat berhasil dengan menggunakan Export
. Hal ini karena waktu tunggu lebih lama dan
jejak memori yang diizinkan lebih besar saat berjalan di sistem batch (tempat
ekspor berjalan). (Ada pendekatan lain yang dapat Anda coba terlebih dahulu seperti yang dijelaskan
dalam dokumen proses debug). Melanjutkan
contoh sebelumnya, misalkan kamus menampilkan error. Anda bisa mendapatkan hasil dengan melakukan hal seperti:
var link = '86836482971a35a5e735a17e93c23272';
Export.table.toDrive({
collection: ee.FeatureCollection([ee.Feature(null, stats)]),
description: 'exported_stats_demo_' + link,
fileFormat: 'CSV'
});
Perhatikan bahwa link disematkan ke dalam nama aset, untuk reproduksi. Perhatikan juga bahwa jika ingin mengekspor toAsset
, Anda harus menyediakan geometri, yang dapat berupa apa saja, misalnya centroid gambar, yang kecil dan murah untuk dihitung. (yaitu, jangan gunakan geometri yang rumit jika Anda tidak memerlukannya).
Lihat halaman proses debug untuk mengetahui contoh penggunaan Export
guna menyelesaikan Waktu tunggu komputasi habis dan Terlalu banyak agregasi serentak. Lihat dokumen ini untuk mengetahui detail tentang
ekspor secara umum.
Jika Anda tidak perlu memotong, jangan gunakan clip()
Menggunakan clip()
secara tidak perlu akan meningkatkan waktu komputasi. Hindari clip()
kecuali jika diperlukan untuk analisis Anda. Jika Anda tidak yakin, jangan memotong. Contoh
penggunaan klip yang buruk:
Jangan memotong input secara tidak perlu.
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);
Pemotongan gambar input dapat dilewati sepenuhnya, karena region
ditentukan dalam panggilan reduceRegion()
:
Tentukan region untuk output.
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);
Jika waktu tunggu komputasi ini habis, Export
-kan seperti dalam contoh
ini.
Jika Anda perlu memotong dengan koleksi yang kompleks, gunakan clipToCollection()
Jika Anda benar-benar perlu memangkas sesuatu, dan geometri yang ingin digunakan untuk
pemangkasan berada dalam koleksi, gunakan 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);
JANGAN gunakan featureCollection.geometry()
atau featureCollection.union()
pada
koleksi besar dan/atau kompleks, yang dapat lebih banyak menggunakan memori.
Jangan gunakan koleksi yang kompleks sebagai region untuk reducer
Jika Anda perlu melakukan pengurangan spasial sehingga pengurangan menggabungkan input dari
beberapa region dalam FeatureCollection
, jangan berikan
featureCollection.geometry()
sebagai input geometry
ke pengurangan. Sebagai gantinya,
gunakan clipToCollection()
dan region yang cukup besar untuk mencakup batas
koleksi. Contoh:
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.
Menggunakan errorMargin
yang bukan nol
Untuk operasi geometri yang mungkin mahal, gunakan margin error terbesar yang memungkinkan dengan presisi komputasi yang diperlukan. Margin error menentukan error maksimum yang diizinkan (dalam meter) selama operasi pada geometri (misalnya, selama re-proyeksi). Menentukan margin error yang kecil dapat menyebabkan kebutuhan untuk memadatkan geometri (dengan koordinat), yang dapat memerlukan banyak memori. Sebaiknya tentukan margin error sebesar mungkin untuk komputasi Anda:
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');
Jangan gunakan skala yang sangat kecil dengan reduceToVectors()
Jika Anda ingin mengonversi raster menjadi vektor, gunakan skala yang sesuai. Menentukan skala yang sangat kecil dapat meningkatkan biaya komputasi secara signifikan. Tetapkan skala setinggi mungkin untuk memberikan presisi yang diperlukan. Misalnya, untuk mendapatkan poligon yang mewakili daratan global:
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');
Pada contoh sebelumnya, perhatikan penggunaan poligon non-geodesik untuk digunakan dalam pengurangan global.
Jangan gunakan reduceToVectors()
dengan reduceRegions()
Jangan gunakan FeatureCollection
yang ditampilkan oleh reduceToVectors()
sebagai input ke
reduceRegions()
. Sebagai gantinya, tambahkan band yang ingin Anda kurangi sebelum memanggil
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));
Perhatikan bahwa cara lain untuk mengurangi piksel satu gambar dalam zona gambar lain meliputi reduceConnectedCommponents() dan/atau pengurangnya.
Menggunakan fastDistanceTransform()
untuk operasi lingkungan
Untuk beberapa operasi konvolusi, fastDistanceTransform()
mungkin lebih efisien
daripada reduceNeighborhood()
atau convolve()
. Misalnya, untuk melakukan erosi dan/atau
dilation input biner:
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');
Menggunakan pengoptimalan di reduceNeighborhood()
Jika Anda perlu melakukan konvolusi dan tidak dapat menggunakan fastDistanceTransform()
,
gunakan pengoptimalan di 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');
Jangan mengambil sampel data lebih dari yang Anda butuhkan
Jangan tergoda untuk meningkatkan ukuran set data pelatihan Anda secara tidak perlu. Meskipun meningkatkan jumlah data pelatihan adalah strategi machine learning yang efektif dalam beberapa situasi, hal ini juga dapat meningkatkan biaya komputasi tanpa peningkatan akurasi yang sesuai. (Untuk memahami kapan harus meningkatkan ukuran set data pelatihan, lihat referensi ini). Contoh berikut menunjukkan bagaimana meminta terlalu banyak data pelatihan dapat menyebabkan error "Nilai yang dihitung terlalu besar" yang mengerikan:
Jangan mengambil sampel terlalu banyak data.
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
Pendekatan yang lebih baik adalah memulai dengan jumlah data yang sedang dan menyesuaikan hyperparameter pengklasifikasi untuk menentukan apakah Anda dapat mencapai akurasi yang diinginkan:
Sesuaikan hyperparameter.
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
}));
Dalam contoh ini, pengklasifikasi sudah sangat akurat, sehingga tidak perlu banyak
penyesuaian. Sebaiknya pilih hierarki terkecil (yaitu
minLeafPopulation
terbesar) yang masih memiliki akurasi yang diperlukan.
Export
hasil perantara
Misalnya, tujuan Anda adalah mengambil sampel dari gambar komputasi
yang relatif kompleks. Sering kali lebih efisien untuk Export
gambar toAsset()
, memuat
gambar yang diekspor, lalu mengambil sampel. Contoh:
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...
Dalam contoh ini, perhatikan bahwa gambar diekspor sebagai float. Jangan mengekspor dengan presisi ganda kecuali jika benar-benar diperlukan. Saat melakukan ekspor ini, perhatikan bahwa link Code Editor (diperoleh tepat sebelum ekspor) disematkan dalam nama file untuk reproduksi.
Setelah ekspor selesai, muat ulang aset dan lanjutkan pengambilan sampel dari
aset tersebut. Perhatikan bahwa sampel yang sangat kecil di area pengujian yang sangat kecil dijalankan terlebih dahulu, untuk
proses debug. Jika berhasil, ambil sampel yang lebih besar dan ekspor.
Sampel besar seperti itu biasanya perlu diekspor. Jangan berharap sampel tersebut
tersedia secara interaktif (misalnya melalui print()
) atau dapat digunakan (misalnya
sebagai input ke pengklasifikasi) tanpa mengekspornya terlebih dahulu.
Join vs. filter peta
Misalnya, Anda ingin menggabungkan koleksi berdasarkan waktu, lokasi, atau beberapa properti metadata. Umumnya, hal ini dilakukan dengan join secara paling efisien. Contoh berikut melakukan penggabungan spasial-temporal antara koleksi Landsat 8 dan Sentinel-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);
Meskipun Anda harus mencoba join terlebih dahulu (Export
jika diperlukan), terkadang
filter()
dalam map()
juga dapat efektif, terutama untuk koleksi
yang sangat besar.
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()
vs. reduceRegions()
vs. loop for
Memanggil reduceRegions()
dengan FeatureCollection
yang sangat besar atau rumit sebagai
input dapat menyebabkan error "Nilai yang dihitung terlalu besar" yang mengerikan. Salah satu
potensial solusinya adalah memetakan reduceRegion()
di atas FeatureCollection
. Solusi potensial lainnya adalah menggunakan loop for. Meskipun hal ini sangat tidak dianjurkan di Earth Engine seperti yang dijelaskan di sini, di sini, dan di sini, reduceRegion()
dapat diterapkan dalam loop for untuk melakukan pengurangan besar.
Misalkan tujuan Anda adalah mendapatkan rata-rata piksel (atau statistik apa pun) di setiap fitur dalam FeatureCollection
untuk setiap gambar dalam ImageCollection
.
Contoh berikut membandingkan tiga pendekatan yang dijelaskan sebelumnya:
// 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());
Perhatikan bahwa hal first()
dari setiap koleksi dicetak, untuk tujuan
proses debug. Anda tidak boleh berharap bahwa hasil lengkap akan tersedia secara interaktif: Anda harus Export
. Perhatikan juga bahwa loop for harus digunakan
dengan sangat hati-hati dan hanya sebagai upaya terakhir. Terakhir, loop for memerlukan
ukuran pengumpulan input yang diperoleh secara manual dan melakukan hardcoding di
lokasi yang sesuai. Jika salah satu dari hal tersebut terdengar tidak jelas bagi Anda, jangan gunakan loop for.
Menggunakan perbedaan maju untuk tetangga dalam waktu
Misalkan Anda memiliki ImageCollection
yang diurutkan secara temporal (yaitu deret waktu) dan
ingin membandingkan setiap gambar dengan gambar sebelumnya (atau berikutnya). Daripada menggunakan
iterate()
untuk tujuan ini, mungkin lebih efisien untuk menggunakan perbedaan
maju berbasis array. Contoh berikut menggunakan metode ini untuk menghapus duplikat
koleksi Sentinel-2, dengan duplikat didefinisikan sebagai gambar dengan hari
yang sama dalam setahun:
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));
Periksa koleksi yang dicetak untuk memverifikasi bahwa duplikat telah dihapus.