advanced
JavaScript
Processing satellite imagery
Prerequisite
Familiarity with front-end development concepts. Some advanced JavaScript required.

This guide will teach you the basics of image processing for mapping, including how to find, process, and upload satellite imagery. Our final product will be a map visualizing landscape change in Dubai from the early 2000s to the present. To do this you’ll use imagery from the NASA Landsat 7 and Landsat 8 satellites to create georeferenced true color composite images using the red, green and blue bands to prioritize the natural look of land and water. Landsat 7 and Landsat 8 collect publicly-available satellite imagery and are the two most modern Landsat satellites from NASA.

If you’re new to working with satellite imagery or are unfamiliar with bands or the raster data type, check out the How satellite imagery works guide before getting started. You’ll need to have a basic familiarity with the command line for this tutorial. If you’re new to the command line, check out a resource like Codecademy to learn more.

Getting started

Besides utilities like brew and tar, there are a few other things you’ll need before getting started:

  • GDAL, a low-level GIS toolkit.
    • You can use the command brew install gdal to install GDAL.
  • libgeotiff, a library for reading and writing coordinate system information to and from GeoTIFF files.
    • You can use the command brew install libgeotiff to install libgeotiff.
  • ImageMagick, an image processing package
    • You can use the command brew install imagemagick -with--libtiff to install ImageMagick.
  • USGS account. You’ll need this to download imagery from EarthExplorer. Check out A Quick Guide to EarthExplorer for Landsat 8 for more information.
  • Your API access token. You can find your access token on your Mapbox account page.

Get ready to use EarthExplorer

Satellite imagery is cut into scenes, which are near-square images covering an area that varies in size depending on the satellite and sensor that collected the information. For Landsat images, which you’ll use in this guide, the coverage is about 170 × 185 kilometers (105 × 115 miles) per scene. You can think of a scene as a single frame from a camera.

If you’ve never used EarthExplorer before, we recommend checking out A Quick Guide to EarthExplorer for Landsat 8 from Robert Simmon, a data visualization expert at NASA’s Earth Observatory.

For this guide, you’ll compare historical and present-day images of Dubai in the United Arab Emirates. Head to EarthExplorer and see if you can find images before and after Dubai’s period of economic growth in the early 2000s.

Download scenes from EarthExplorer

Following Simmon’s guide, you should be able to find that Landsat 7 scene LE71600432000140SGS00 from late May 2000 and Landsat 8 scene LC81600432014154LGN00 from early June 2014, which are both cloud-free images that suit the purposes of this tutorial well.

Landsat Archive Landsat Scene Identifier
L7 ETM+ SLC-on (1999-2003) LE71600432000140SGS00
L8 OLI/TIRS LC81600432014154LGN00

Download the Level 1 product of each of these scenes from EarthExplorer:

  • From EarthExplorer, click the Data Sets tab on the left-side panel.
  • Click Landsat Archive to reveal more options.
  • Check the Landsat archive name shown in the table above (you’ll need to follow this process twice).
  • Click Additional Criteria.
  • Enter the Landsat Scene Identifier shown in the table above (you’ll need to follow this process twice).
  • Click Results.
  • Click the download icon (the green arrow pointing down).
  • Click Download Level 1 Product. Note that these are very large image fields and may take several hours to process.

Make sure you follow this process for both the Landsat 7 and Landsat 8 images we’ll be working with.

Examine the bundle contents

The Level 1 products from Landsat 7 and Landsat 8 are compressed .tar.gz files known as bundles. The bundle will unpack into a directory of 13 items, mostly TIFF images, with each name starting with the 21-digit scene ID and ending with the band number. For example, LE71600432000140SGS00_B1.TIF is the Band 1 readout for scene LE71600432000140SGS00. Here’s what the raw file looks like:

Landsat scene LE71600432000140SGS00_B1

Process Landsat 7 imagery

Now that you have the images you want, you can process them to improve their quality and clarity. You’ll work entirely from the command line for this section, starting with the Landsat 7 bundle you downloaded above. On Landsat 7, the red, green, and blue bands are numbered 3, 2, and 1. In the following steps, you’ll reproject these bands into the Web Mercator (EPSG:3857) projection, composite the bands into a single RGB image, optimize the image’s color and rendering at multiple zoom levels, georeference, and remove the black background.

First, create a variable for your scene ID:

  1. Open a new Terminal window
  2. Navigate to the folder where your Landsat 7 bundle is stored
  3. Create an id variable for our scene ID by typing the following into the command line and pressing Enter:
id="LE71600432000140SGS00"

Next, convert each band into the Web Mercator projection. Loop through the TIFF files for Bands 1-3, reproject them into Web Mercator (EPSG:3857) projection, and export them to a new file by copying and pasting the following command:

for BAND in {3,2,1}; do
gdalwarp -t_srs EPSG:3857 $id"_B"$BAND.tif $BAND-projected.tif;done

The new projected files will be named 1-projected.tif, 2-projected.tif, and 3-projected.tif, where the first digit represents the Band number. Merge all three projected files into a single composited RGB image with gdal_merge.py with the following command:

gdal_merge.py -v -separate -of GTiff -co PHOTOMETRIC=RGB -o $id-RGB.tif 3-projected.tif 2-projected.tif 1-projected.tif

Next, take a look at your new RGB image LE71600432000140SGS00-RGB.tif:

Landsat 7 images of Dubai

It’s a little hazy, but overall the color, saturation, and contrast are pretty good. Next, use ImageMagick to tweak the image just a bit. Modify the color, saturation, and contrast of the image with the following command:

convert -channel RGB -modulate 100,150 -sigmoidal-contrast 4x50% $id-RGB.tif $id-RGB-cc.tif

This command uses several of ImageMagick’s many functions:

  • The -channel flag makes sure you’re working with all three bands at the same time.
  • The -modulate flag adjusts overall brightness and saturation.
  • The -sigmoidal-contrast flag controls contrast and establishes how far into your value-range you’d like middle brightness to be.

Note that this operation doesn’t create a new file, it just modifies your existing image. Take a look at your image now:

Modified Landsat image of Dubai

Those small corrections make for a much better looking image, but you’re not finished yet.

Next, use a cubic downsampling method to build overviews using the GDAL command gdaladdo. Building overviews optimizes large images by inserting reduced resolution copies of the image inside the original file that can be used in place of full resolution images at low zoom levels. This process helps maintain a quick load time by not requiring unnecessary detail to load at each zoom level.

To build overviews of your current file and export to a new file called LE71600432000140SGS00-RGB-cc.tif, use the following command:

gdaladdo -r cubic $id-RGB-cc.tif 2 4 8 10 12

ImageMagick strips images of the spatial information that locates them in the world. To account for this ImageMagick quirk and georeference your image again, compare it to the Band 3 image you reprojected with gdalwarp in one of your previous steps. It doesn’t matter which image you choose to georeference from, so long as its projection is consistent with your desired output.

Take the spatial reference from the file 3-projected.tif and rewrite it to a .tfw (TIFF worldfile) with the same name with the following command:

listgeo -tfw 3-projected.tif

Rename the TIFF worldfile to have same name as the file that needs to be georeferenced (LE71600432000140SGS00-RGB-cc.tif):

mv 3-projected.tfw $id-RGB-cc.tfw

By renaming your .tfw file to match the name of a TIFF that isn’t internally georeferenced, GDAL will automatically give the non-georeferenced file the correct spatial reference.

  • Apply the Web Mercator spatial reference system to your non-georeferenced file using the following command:
gdal_edit.py -a_srs EPSG:3857 $id-RGB-cc.tif

As a last step, remove the black background surrounding our image:

gdalwarp -srcnodata 0 -dstalpha $id-RGB-cc.tif $id-RGB-cc-2.tif

Landsat 7 image without black background

This exports your final image to a new file, LE71600432000140SGS00-RGB-cc-2.tif.

Congratulations! You’ve successfully processed your Landsat 7 image! Next up is your Landsat 8 image.

Process Landsat 8 imagery

You will repeat much of the process we just went through for your Landsat 8 image with one important addition. Landsat 8 data is collected by a sensor that has a higher radiometric resolution than the sensor on Landsat 7. This means that Landsat 8 data comes in a 16-bit format, while Landsat 7 comes as 8-bit. Because Mapbox only accepts 8-bit resolution images, you need to convert your 16-bit Landsat 8 bands into 8-bit.

To reformat the image, you need to take the value range in the 16-bit image (0 - 65,535) and convert it to the same value range as an 8-bit image (0 - 255). The next steps follow the same process as the Landsat 7 image, but apply gdal_translate commands to rescale the bands to the 8-bit format.

Though you can execute these commands line by line, for this step we suggest executing the script all at once since most of the commands are very similar to how you processed your Landsat 7 imagery. To do this, you’ll need to download the full script and move it to the directory where your Landsat 8 imagery bundle is stored

Download Shell script

To execute the script, navigate to that same directory and use the following command to give permission for this script to run and then run it:

chmod +x process-landsat-8.sh
./process-landsat-8.sh

Each line of the script is commented so you can following along step by step. The final output of this process is an optimized, georeferenced imagery file named LC81600432014154LGN00-RGB-scaled-cc-2.tif Here’s the original Landsat 8 RGB file and the processed image:

rasters-L8-dubai
rasters-L8-dubai-cc

As you can see, this process creates a much clearer, more legible image.

Build a map using Mapbox GL JS

Now that your images are processed and ready to upload, it’s time to get them into your Mapbox account.

Upload to Mapbox Studio

Congratulations on finishing your image processing! Now that you have vivid, georeferenced images, upload them to your Mapbox account as a tileset so you can use them in your projects. The following steps walk you through how to upload your GeoTIFFs from your Mapbox tilesets page, though you can also upload using the Mapbox Uploads API if you prefer.

  • Navigate to mapbox.com/tilesets
  • Click the New tileset button
  • A new window will open. Select the final Landsat 7 file from your image processing steps and click Upload
  • Repeat the above steps with the final Landsat 8 file from your image processing steps

You can find the images you uploaded on your tilesets page. Each tileset will have its own map ID which you can find by clicking the hamburger menu to the right of the listed tileset. You can use the map ID to reference a tileset when using developer tools, such as Mapbox GL JS or the Mapbox iOS or Android SDKs. For the next steps, you’ll use the map id from your Landsat 7 and Landsat 8 tilesets to build a map with Mapbox GL JS.

Compare tilesets with Mapbox GL JS

Next, add your uploaded raster tilesets to a map and use the Mapbox GL Compare plug-in to compare your Landsat 7 and Landsat 8 imagery.

Create a new HTML file in your text editor to initialize a new Mapbox GL JS map. If you’re new to writing code, check out the Get ready to write code section of the Add points to a map tutorial. If you’re new to Mapbox GL JS, we recommend reviewing Mapbox GL JS fundamentals before you get started.

  • Open your text editor.
  • Copy and paste the code below into your text editor to initialize your Mapbox GL JS map.
  • Replace {access token} with your own access token.
    • You can find your access token on your account page.
  • Replace {before map ID} with the map ID of your Landsat 7 tileset.
    • You can find your map ID from your tilesets page.
  • Replace {after map ID} with the map ID of your Landsat 8 tileset.
    • You can find your map ID from your tilesets page.
  • Save your file as index.html.
  • Open the file in a browser.
  • You should see your initialized Mapbox GL JS map displaying in a browser window.
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8' />
    <title></title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.38.0/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.38.0/mapbox-gl.css' rel='stylesheet' />
    <style>
      body {
        margin: 0;
        padding: 0;
      }

      #map {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 100%;
      }
    </style>
</head>
<body>

<style>
body {
  overflow: hidden;
}

body * {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.map {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
}
</style>
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-compare/v0.1.0/mapbox-gl-compare.js'></script>
<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-compare/v0.1.0/mapbox-gl-compare.css' type='text/css' />

<div id='before' class='map'></div>
<div id='after' class='map'></div>

<script>
mapboxgl.accessToken = '{access token}';

var beforeTileset = '{before map ID}';
var afterTileset = '{after map ID}';

var beforeMap = new mapboxgl.Map({
  container: 'before',
  style: {
    version: 8,
    sources: {
      'raster-tiles': {
        type: 'raster',
        url: 'mapbox://' + beforeTileset,
        tileSize: 256
      }
    },
    layers: [{
      id: 'simple-tiles',
      type: 'raster',
      source: 'raster-tiles',
      minzoom: 0,
      maxzoom: 22
    }]
  },
  center: [55.1720, 25.0859],
  zoom: 11
});

var afterMap = new mapboxgl.Map({
  container: 'after',
  style: {
    version: 8,
    sources: {
      'raster-tiles': {
        type: 'raster',
        url: 'mapbox://' + afterTileset,
        tileSize: 256
      }
    },
    layers: [{
      id: 'simple-tiles',
      type: 'raster',
      source: 'raster-tiles',
      minzoom: 0,
      maxzoom: 22
    }]
  },
  center: [55.1720, 25.0859],
  zoom: 11
});

var map = new mapboxgl.Compare(beforeMap, afterMap, {});

</script>

</body>
</html>

Finished product

Nice work! You just found your own satellite imagery, processed it for best visual impact, and created a compelling display to visualize landscape change over time. You also learned how to use command-line analysis tools to process any kind of raster imagery you might want to use with Mapbox.

Next steps

If you enjoyed this guide, we recommend checking out Georeferencing imagery, which will walk you through a manual process for georeferencing imagery or raster data that doesn’t already come with a geospatial reference system. We also recommend checking out our Mapbox GL JS examples for ideas to extend your web applications even further.