As we prepare our MapBox Satellite Live service, we’re continuing to expand our satellite imagery processing to include the best and newest sensors available – this time with SPOT 6 from Astrium. SPOT 6 shares the same orbit as Pléiades, which we processed last week. SPOT 6 captures more area per shot, while Pléiades has better resolution. SPOT 6 is a very agile companion in combination with Pléiades or when you are interested in a wide area.

The region we are processing is around Morro da Boa Vista, Brazil. On July 22nd, this region witnessed snow, something that has only been recorded twice before (in 2005 and in 1965). The snow melted in a few days, but SPOT 6 was able to snap this image the morning after the snow, on July 23rd at 10 a.m. local time.

The SPOT 6 satellite shares the same orbit as the Pléiades twins (which we covered last week), only shifted between them in the orbit. Pléiades offers 20 km × 20 km images at 0.5 m resolution, while SPOT captures 60 km × 60 km footprints at 1.5 m. Together with SPOT 7 – the twin of SPOT 6, due to launch this year – these four satellites evenly spaced on the same orbit cover the whole earth twice daily.

This combination of satellites responds to different needs for contextual or focused images. For example, wide area flooding can be imaged every day by SPOT satellites, while Pléiades can complement this with very high resolution updates of populated areas and daily revisit capability.

## Astrium’s constellation offers daily revisit of any point, a ground segment with 6 daily uplink opportunities, and 6 million km2 of imagery captured every day.

Whether you task SPOT 6 or acquire an image from the massive SPOT archive, the source imagery can be processed in a matter of minutes into a finished map. Astrium provides a very useful technical description of SPOT 6 images. Our source imagery was delivered both pansharpened and orthorectified by Astrium, and divided into four tile subsets. In the steps below, I’ll walk you through the steps to process each tile.

The step-by-step process is similar to the one we covered last week:

  • Reproject into Web Mercator tiles
  • Adjust color
  • Render with TileMill and upload to MapBox.com

1. Install all the software you need.

If you are on OS X this should get you going (use sudo apt-get instead of brew if you are on Ubuntu):

brew install imagemagick --with-libtiff

2. Reproject to Web Mercator

I’m going to remove the 4th layer since in this particular guide we are not interested in the infrared channel. In this step I also specify I want a Byte unit (not Int) and we scale all images to the same intensity. I used gdalinfo -stats on the images to take a look at the brightness values. An average around 200 and a maximum of around 3000 already tells me that the image is on the dark side (shadowed valleys) but with bright spots (snowy peaks). This will make the color correction a bit complicated when we want to retain clear detail across intensity levels. Lastly, on most of these steps, we’ll be using virtual rasters to save a lot of time and reprocessing. Here’s a complete translation function:

gdal_translate \
    -b 1 \
    -b 2 \
    -b 3 \
    -ot Byte \
    -scale 0 3000 0 255 \
    -co PHOTOMETRIC=RGB \
    -of VRT \
    $INPUT \
    ${INPUT%.tif}.vrt;

Note: we use the bash % substring removal here to give the output file a different filename from the input image. This is a convenient way to set input and output from a single variable.

We use gdalwarp to transform the imagery from EPSG:32722, its original projection (which can be found with gdalinfo – it will be different for different imagery), into Web Mercator, EPSG:3857. We also take this opportunity to add overviews, which speed up some operations.

function warp(){
    INPUT=$1;
    OUTPUT=$2;

    gdalwarp \
        -q \
        -s_srs EPSG:32722 \
        -t_srs EPSG:3857 \
        $INPUT \
        $OUTPUT;

    gdaladdo \
        -q\
        -r cubic \
        $OUTPUT \
        2 4 8 16;
}

To invoke this function, run warp $INPUT ${INPUT%.tif}_3857.tif for each tile.

3. Color Adjustment

The last step is to adjust the color of the rasters. This one is particularly tricky. The region we are observing has deep valleys and many shades of green. The local sun elevation is 30° and thus shadows appear, giving the darkest parts of the image very fine contrast levels. On top of that, the peaks have highly reflective snow, and so the brightest parts of the image also has very fine contrast levels. After a few iterations I found this convert combination to increase the imagery’s dynamic range across intensities, avoid saturation on each level, and keep colors natural.

convert \
     -modulate 100,120 \
     -channel B -gamma 0.85 \
     -channel RGB \
     -sigmoidal-contrast 9x10% \
     -gamma 1.2

ImageMagick’s convert, however, does not understand geocoordinates on images, so we need to recover those after adjusting the color. To do that we copy over the .tfw world file and we use gdal_translate with the color-adjusted tiles. As before, we create overviews for each tile. The combined process looks like this:

function color_correct(){
    INPUT=$1;
    mkdir -p color;
    listgeo -tfw $INPUT 2> /dev/null;
    cp ${INPUT%.tif}.tfw color/${INPUT%.tif}.tfw;
    convert \
        -modulate 100,120 \
        -channel B -gamma 0.85 \
        -channel RGB \
        -sigmoidal-contrast 9x10% \
        -gamma 1.2 \
        $INPUT -compress lzw color/$INPUT 2> /dev/null;

    gdal_translate \
        -q \
        -a_srs EPSG:3857 \
        -a_nodata "0 0 0" \
        -of VRT \
        color/$INPUT \
        color/${INPUT%.tif}.vrt;
    rm color/$INPUT;

    gdaladdo \
        -q \
        -r cubic \
        color/${INPUT%.tif}.vrt \
        2 4 8 16;
}

After color-correcting each tile, by running color_correct ${INPUT%.tif}_3857.tif from the warping step above, the last step is to create a virtual raster of the finished tiles:

  gdalbuildvrt \
    color.vrt \
    $(pwd)/color/*.vrt;

With our mosaic VRT created, we are ready to add the SPOT 6 imagery to TileMill. The CartoCSS is 2 lines of code:

#color {
  raster-opacity: 1;
  raster-scaling: lanczos;
}

We use raster-scaling: lanczos in the CartoCSS to specify how Mapnik behaves when interpolating pixel values.

Finished Map


Check out our increasing array of processing guidelines and drop us a note if you have other sources. We’re also streamlining the tasking system to get your data from space as quickly and easily as possible – no fax machine required.

Ping us on Twitter with ideas and feedback: Chris (@hrwgc), Charlie (@vruba), or myself (@brunosan).