Earth Engine is designed so that you rarely have to worry about map projections when
doing computations. As with scale, the projection in which computations take place is
determined on a "pull" basis. Specifically, inputs are requested in the output
projection. The output may be determined from a function parameter (e.g. crs
),
Code Editor and geemap map objects (which have a maps
mercator (EPSG:3857) projection), or with a reproject()
call. When you
display images in the Code Editor or geemap, inputs are requested in
maps mercator. Consider the
following simple operation on a MODIS image, which has a
sinusoidal projection:
Code Editor (JavaScript)
// The input image has a SR-ORG:6974 (sinusoidal) projection. var image = ee.Image('MODIS/061/MOD13A1/2014_05_09').select(0); // Normalize the image and add it to the map. var rescaled = image.unitScale(-2000, 10000); var visParams = {min: 0.15, max: 0.7}; Map.addLayer(rescaled, visParams, 'Rescaled');
import ee import geemap.core as geemap
Colab (Python)
# The input image has a SR-ORG:6974 (sinusoidal) projection. image = ee.Image('MODIS/061/MOD13A1/2014_05_09').select(0) # Normalize the image and add it to the map. rescaled = image.unitScale(-2000, 10000) vis_params = {'min': 0.15, 'max': 0.7} m = geemap.Map() m.add_layer(rescaled, vis_params, 'Rescaled') m
The order of operations for this code sample is diagrammed in Figure 1. Note that the projection of the input is determined by the output, specifically the maps mercator projection of the map display in the Code Editor. This projection propagates back through the sequence of operations such that the inputs are requested in maps mercator, at a scale determined by the zoom level of the map.
In Earth Engine, projections are specified by a Coordinate Reference System (CRS or
the crs
parameter of many methods). You can check the projection of an
image by calling projection()
on it:
Code Editor (JavaScript)
var image = ee.Image('LANDSAT/LC08/C02/T1_TOA/LC08_044034_20140318').select(0); print('Projection, crs, and crs_transform:', image.projection()); print('Scale in meters:', image.projection().nominalScale());
import ee import geemap.core as geemap
Colab (Python)
image = ee.Image('LANDSAT/LC08/C02/T1_TOA/LC08_044034_20140318').select(0) display('Projection, crs, and crs_transform:', image.projection()) display('Scale in meters:', image.projection().nominalScale())
Note that by calling nominalScale()
on the ee.Projection
returned
by projection()
, you can determine the native resolution of the image. The
native resolution is the nominal pixel scale in meters of the lowest level of the
image pyramid. Because each band of an image can have a
different scale and/or projection, if you call projection()
on an image
with at least one band that doesn't have the same projection as the others, you may see an
error like:
The default projection
Unless you need your computation to occur in a specific projection, there is generally
no need to specify a projection. Only for output that's ambiguous will Earth Engine
require you to specify a projection and/or scale. Ambiguity can result from
reducing an ImageCollection
containing images with different projections
(i.e. creating a composite). An image which is a
composite or mosaic of input images with different projections will have the default
projection, which is WGS84 with 1-degree scale.
For example:
Code Editor (JavaScript)
var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA'); var mosaic = collection.filterDate('2018-01-01', '2019-01-01').mosaic(); print(mosaic.projection());
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA') mosaic = collection.filterDate('2018-01-01', '2019-01-01').mosaic() display(mosaic.projection())
If you try to use an image like this in a computation, you may see an error like:
Generally, an aggregation at 1-degree scale is not desired or intended, so Earth Engine gives this friendly reminder to provide a complete specification for the output.
Users often find this behavior confusing and worry about the "lost" projection information, but the pixels aren't actually computed until they're needed (learn more), and at that point, there's always an output projection that accompanies the request that specified how to compute the composite.
In the vast majority of use cases, having no projection is not a problem and is actually a valuable optimization, as it allows previewing the results at any zoom level without having to wait for the full resolution computation to complete. But it does mean that the output can appear different at different zoom levels.
If the optimized display image somehow isn't sufficient, computation in a specific projection can be forced by reprojecting the output as described in the following section.
Reprojecting
You can force operations to be performed in a specific projection with the
reproject()
method. Using reproject()
results in the inputs
being requested in the projection specified in the reproject()
call.
Computations in your code before the reproject()
call will be done
in the specified projection. For example, to force a composite to be produced in a
specific projection:
Code Editor (JavaScript)
// Some projection that is suitable for your area of interest. var proj = ee.Projection(...); var output = collection.reduce(...).reproject(proj);
import ee import geemap.core as geemap
Colab (Python)
# Some projection that is suitable for your area of interest. proj = ee.Projection(...) output = collection.reduce(...).reproject(proj)
A few cases that require a fixed projection include:
- Computing gradients (e.g.
ee.Terrain.gradient
oree.Terrain.slope
). reduceResolution
, for when you want to aggregate higher resolution pixels into lower resolution. (Learn more about reducing resolution).
There are several reasons you should avoid using reproject()
unless you
absolutely need to. Suppose, for example, you reproject something and add it to the
map. If the scale you specified in the reproject()
call is much smaller
than the zoom level of the map, Earth Engine will request all the inputs at very small
scale, over a very wide spatial extent. This can result in much too much data being
requested at once and lead to an error.
If the eventual output is in a different projection from that specified in the
reproject()
call, that will result in another reprojection. This is
another reason to be cautious about using reproject()
in your code. Consider
the following example, which forces the MODIS image to first be reprojected to
WGS84, then reprojected to
maps mercator for display in the Code Editor map:
Code Editor (JavaScript)
// The input image has a SR-ORG:6974 (sinusoidal) projection. var image = ee.Image('MODIS/061/MOD13A1/2014_05_09').select(0); // Operations *before* the reproject call will be done in the projection // specified by reproject(). The output results in another reprojection. var reprojected = image .unitScale(-2000, 10000) .reproject('EPSG:4326', null, 500); Map.addLayer(reprojected, {min: 0.15, max: 0.7}, 'Reprojected');
import ee import geemap.core as geemap
Colab (Python)
# The input image has a SR-ORG:6974 (sinusoidal) projection. image = ee.Image('MODIS/061/MOD13A1/2014_05_09').select(0) # Operations *before* the reproject call will be done in the projection # specified by reproject(). The output results in another reprojection. reprojected = image.unitScale(-2000, 10000).reproject('EPSG:4326', None, 500) m = geemap.Map() m.add_layer(reprojected, {'min': 0.15, 'max': 0.7}, 'Reprojected') m
Figure 2 diagrams the flow of operations corresponding to this simple reprojection
example. Note that the first reprojection is explicit, as specified in the
reproject()
call. The second reprojection is implicit, performed by
Earth Engine automatically in order to display the result on the map. Also observe that the
information about what projection to use propagates back from the request to the input.