Announcement: All noncommercial projects registered to use Earth Engine before
April 15, 2025 must
verify noncommercial eligibility to maintain Earth Engine access.
Save-Best Joins
Stay organized with collections
Save and categorize content based on your preferences.
To save only the best match for each element in a collection, use an
ee.Join.saveBest()
. The saveBest()
join functions in an
equivalent way to the saveAll()
join, except for each element in the
primary
collection, it saves the element from the secondary
collection with the best match. Unmatched elements in the primary collection are
dropped. Suppose the intention is to find a meteorological image closest in time to each
Landsat image in the primary
collection. To perform this join, the
ee.Filter
must be redefined for a single join condition (combined filters
will not work with saveBest()
since it is ambiguous how to combine ranks
from multiple sub-Filters):
Code Editor (JavaScript)
// Load a primary collection: Landsat imagery.
var primary = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA')
.filterDate('2014-04-01', '2014-06-01')
.filterBounds(ee.Geometry.Point(-122.092, 37.42));
// Load a secondary collection: GRIDMET meteorological data
var gridmet = ee.ImageCollection('IDAHO_EPSCOR/GRIDMET');
// Define a max difference filter to compare timestamps.
var maxDiffFilter = ee.Filter.maxDifference({
difference: 2 * 24 * 60 * 60 * 1000,
leftField: 'system:time_start',
rightField: 'system:time_start'
});
// Define the join.
var saveBestJoin = ee.Join.saveBest({
matchKey: 'bestImage',
measureKey: 'timeDiff'
});
// Apply the join.
var landsatMet = saveBestJoin.apply(primary, gridmet, maxDiffFilter);
// Print the result.
print(landsatMet);
Python setup
See the
Python Environment page for information on the Python API and using
geemap
for interactive development.
import ee
import geemap.core as geemap
Colab (Python)
# Load a primary collection: Landsat imagery.
primary = (
ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA')
.filterDate('2014-04-01', '2014-06-01')
.filterBounds(ee.Geometry.Point(-122.092, 37.42))
)
# Load a secondary collection: GRIDMET meteorological data
gridmet = ee.ImageCollection('IDAHO_EPSCOR/GRIDMET')
# Define a max difference filter to compare timestamps.
max_diff_filter = ee.Filter.maxDifference(
difference=2 * 24 * 60 * 60 * 1000,
leftField='system:time_start',
rightField='system:time_start',
)
# Define the join.
save_best_join = ee.Join.saveBest(matchKey='bestImage', measureKey='timeDiff')
# Apply the join.
landsat_met = save_best_join.apply(primary, gridmet, max_diff_filter)
# Print the result.
display(landsat_met)
Note that a saveBest()
join defines the name of the property with which to
store the best match (‘bestImage’
) and the name of the property with which
to store the goodness of the match metric (‘timeDiff’
). Inspection of the
results indicates that a matching DAYMET image has been added to the property
bestImage
for each Landsat scene in the primary
collection. Each
of these DAYMET images has the property timeDiff
indicating the time
difference in milliseconds between the DAYMET image and the Landsat image, which will be
minimum among the DAYMET images passing the condition in the filter.
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2024-12-27 UTC.
[null,null,["Last updated 2024-12-27 UTC."],[[["\u003cp\u003e\u003ccode\u003eee.Join.saveBest()\u003c/code\u003e is used to efficiently match elements from two collections, keeping only the best match for each element in the primary collection based on a specified metric.\u003c/p\u003e\n"],["\u003cp\u003eUnmatched elements in the primary collection are excluded from the results of a \u003ccode\u003esaveBest()\u003c/code\u003e join.\u003c/p\u003e\n"],["\u003cp\u003e\u003ccode\u003esaveBest()\u003c/code\u003e joins require a single filter condition for determining the best match, unlike \u003ccode\u003esaveAll()\u003c/code\u003e joins that can utilize combined filters.\u003c/p\u003e\n"],["\u003cp\u003eThe resulting collection from a \u003ccode\u003esaveBest()\u003c/code\u003e join includes the best matching element stored under a specified property (e.g., 'bestImage') along with a property indicating the match quality (e.g., 'timeDiff').\u003c/p\u003e\n"]]],["`ee.Join.saveBest()` saves the best-matching element from a secondary collection to each element in a primary collection. It requires a single `ee.Filter` for matching. The provided example joins Landsat imagery with GRIDMET meteorological data, finding the closest meteorological image in time to each Landsat image. The join stores the best-matching image in a property named 'bestImage' and the time difference in a property named 'timeDiff'. Unmatched elements from the primary are dropped.\n"],null,["# Save-Best Joins\n\nTo save only the best match for each element in a collection, use an\n`ee.Join.saveBest()`. The `saveBest()` join functions in an\nequivalent way to the `saveAll()` join, except for each element in the\n`primary` collection, it saves the element from the `secondary`\ncollection with the best match. Unmatched elements in the primary collection are\ndropped. Suppose the intention is to find a meteorological image closest in time to each\nLandsat image in the `primary` collection. To perform this join, the\n`ee.Filter` must be redefined for a single join condition (combined filters\nwill not work with `saveBest()` since it is ambiguous how to combine ranks\nfrom multiple sub-Filters):\n\n### Code Editor (JavaScript)\n\n```javascript\n// Load a primary collection: Landsat imagery.\nvar primary = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA')\n .filterDate('2014-04-01', '2014-06-01')\n .filterBounds(ee.Geometry.Point(-122.092, 37.42));\n\n// Load a secondary collection: GRIDMET meteorological data\nvar gridmet = ee.ImageCollection('IDAHO_EPSCOR/GRIDMET');\n\n// Define a max difference filter to compare timestamps.\nvar maxDiffFilter = ee.Filter.maxDifference({\n difference: 2 * 24 * 60 * 60 * 1000,\n leftField: 'system:time_start',\n rightField: 'system:time_start'\n});\n\n// Define the join.\nvar saveBestJoin = ee.Join.saveBest({\n matchKey: 'bestImage',\n measureKey: 'timeDiff'\n});\n\n// Apply the join.\nvar landsatMet = saveBestJoin.apply(primary, gridmet, maxDiffFilter);\n\n// Print the result.\nprint(landsatMet);\n```\nPython setup\n\nSee the [Python Environment](/earth-engine/guides/python_install) page for information on the Python API and using\n`geemap` for interactive development. \n\n```python\nimport ee\nimport geemap.core as geemap\n```\n\n### Colab (Python)\n\n```python\n# Load a primary collection: Landsat imagery.\nprimary = (\n ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA')\n .filterDate('2014-04-01', '2014-06-01')\n .filterBounds(ee.Geometry.Point(-122.092, 37.42))\n)\n\n# Load a secondary collection: GRIDMET meteorological data\ngridmet = ee.ImageCollection('IDAHO_EPSCOR/GRIDMET')\n\n# Define a max difference filter to compare timestamps.\nmax_diff_filter = ee.Filter.maxDifference(\n difference=2 * 24 * 60 * 60 * 1000,\n leftField='system:time_start',\n rightField='system:time_start',\n)\n\n# Define the join.\nsave_best_join = ee.Join.saveBest(matchKey='bestImage', measureKey='timeDiff')\n\n# Apply the join.\nlandsat_met = save_best_join.apply(primary, gridmet, max_diff_filter)\n\n# Print the result.\ndisplay(landsat_met)\n```\n\nNote that a `saveBest()` join defines the name of the property with which to\nstore the best match (`'bestImage'`) and the name of the property with which\nto store the goodness of the match metric (`'timeDiff'`). Inspection of the\nresults indicates that a matching DAYMET image has been added to the property\n`bestImage` for each Landsat scene in the `primary` collection. Each\nof these DAYMET images has the property `timeDiff` indicating the time\ndifference in milliseconds between the DAYMET image and the Landsat image, which will be\nminimum among the DAYMET images passing the condition in the filter."]]