Navigation überspringen

NAIP

AerialImagery AIforEarth USDA

Luftaufnahmen aus dem National Agricultural Imagery Program (NAIP)

Das NAIP bietet hochauflösende Luftaufnahmen der gesamten USA. Das Programm wird vom Aerial Field Photography Office (AFPO) innerhalb des US Department of Agriculture (USDA) verwaltet. Es sind Daten aus einem Zeitraum von 2010 bis heute verfügbar.

Storage-Ressourcen

Die Daten werden in cloudoptimierten GeoTIFF-Dateien in Azure Blob Storage in einem Rechenzentrum der Region „USA, Osten“ gespeichert. Folgender Blobcontainer wird dazu verwendet:

https://naipblobs.blob.core.windows.net/naip

In diesem Container sind die Daten wie folgt strukturiert:

v002/[state]/[year]/[state]_[resolution]_[year]/[quadrangle]/filename

Beispiel:

v002/al/2015/al_100cm_2015/30086/m_3008601_ne_16_1_20150804.tif

Weitere Details dazu:

  • Year: Vierstellige Jahreszahl. Bilder werden in jedem Bundesstaat alle 3–5 Jahre gesammelt. Für jedes Jahr gibt es also Daten aus einigen (aber nicht allen) Bundesstaaten. Zum Beispiel gibt es für Alabama Daten aus den Jahren 2011 und 2013, aber nicht aus 2012, während es für Kalifornien Daten aus dem Jahr 2012, aber nicht aus 2011 und 2013 gibt. ESRI bietet eine interaktive Karte mit Informationen, welche Gebiete vom NAIP in welchem Jahr abgedeckt wurden.
  • State: Zweistelliges Kürzel des Bundesstaats.
  • Resolution: Die Spezifikation der Bildauflösung in Form einer Zeichenfolge, die im Laufe der Geschichte von NAIP variiert hat. Je nach Jahr und Bundesstaat kann dies “050cm”, “060cm” oder “100cm” sein.
  • Quadrangle: Quadrangle des United States Geological Survey (USGS), das eine Fläche von 7,5 Minuten × 7,5 Minuten definiert.

Dateien werden als cloudoptimierte GeoTIFF-Bilder mit der Erweiterung „.tif“ gespeichert. Diese Dateien wurden aus dem ursprünglichen, vom USDA bereitgestellten Format konvertiert und von ESRI aufbereitet.

Für jedes Bild gibt es auch Miniaturansichten; ersetzen Sie “.tif” durch “.200.jpg” , um die Miniaturansicht abzurufen. Beispiel:

https://naipblobs.blob.core.windows.net/naip/v002/al/2013/al_100cm_2013/30086/m_3008601_ne_16_1_20130928.200.jpg

Ein vollständiges Python-Beispiel für den Zugriff auf und das Zeichnen eines NAIP-Bilds finden Sie im bereitgestellten Notebook unter “Datenzugriff”.

Außerdem wird ein schreibgeschütztes SAS-Token (Shared Access Signature) bereitgestellt, mit dem Sie auf NAIP-Daten zugreifen können. Dafür können Sie beispielsweise die Lösung „BlobFuse“ nutzen, mit der Blobcontainer als Laufwerke eingebunden werden können:

st=2019-07-18T03%3A53%3A22Z&se=2035-07-19T03%3A53%3A00Z&sp=rl&sv=2018-03-28&sr=c&sig=2RIXmLbLbiagYnUd49rgx2kOXKyILrJOgafmkODhRAQ%3D

Die Anleitung zum Einbinden unter Linux finden Sie hier.

NAIP-Daten können mehrere Hundert Terabyte groß sein. Für umfangreiche Verarbeitungen sollte daher das Azure-Rechenzentrum in der Region „USA, Osten“ verwendet werden, wo die Bilder gespeichert sind. Wenn Sie NAIP-Daten für umweltwissenschaftliche Anwendungen nutzen, sollten Sie sich um eine Förderung im Rahmen des AI for Earth-Programms bewerben. Diese unterstützt Sie dabei, die Verarbeitungsanforderungen zu erfüllen.

Index

Eine Liste aller NAIP-Dateien finden Sie als CSV-Datei in dieser ZIP-Datei:

https://naipblobs.blob.core.windows.net/naip-index/naip_v002_index.zip

Außerdem behalten wir ein „rtree“-Objekt zur Unterstützung räumlicher Abfragen für Python-Benutzer bei; Einzelheiten dazu finden Sie im Beispielnotebook.

Sie können die Daten auch hier durchsuchen.

Wohin wurden die MRF-Dateien verschoben?

Im Juni 2020 haben wir unser gesamtes NAPI-Archiv aktualisiert, um sowohl Abdeckung als auch Wartbarkeit zu verbessern. Darüber hinaus haben wir vom MRF-Format auf cloudoptimiertes GeoTIFF umgestellt und einige Änderungen an Pfadstrukturen vorgenommen. Die MRF-Dateien sind vorübergehend weiterhin in einem anderen Container verfügbar. Wenn Sie für Ihre Arbeit wichtig sind, und Sie Zugriff benötigen, wenden Sie sich an aiforearthdatasets@microsoft.com.

Anschauliches Bild


Darstellung mit 1m-Auflösung von einem Gebiet in der Nähe des Microsoft-Campus in Redmond (2017)

Contact

Falls Sie Fragen zu diesem Dataset haben, wenden Sie sich an aiforearthdatasets@microsoft.com.

Access

Available inWhen to use
Azure Notebooks

Quickly explore the dataset with Jupyter notebooks hosted on Azure or your local machine.

Select your preferred service:

Azure Notebooks

Azure Notebooks

Package: Language: Python

Demo notebook for accessing NAIP data on Azure

This notebook provides an example of accessing NAIP data from blob storage on Azure, displaying an image using the rasterio library.

We will demonstrate how to access and plot a tile given a known tile filename, as well as how to access tiles by lat/lon. Finally, we'll demonstrate how to retrieve only the patches you care about from our cloud-optimized image files.

NAIP data are stored in the West Europe Azure region, so this notebook will run most efficiently on Azure compute located in West Europe. We recommend that substantial computation depending on NAIP data also be situated in West Europe. You don't want to download hundreds of terabytes to your laptop! If you are using NAIP data for environmental science applications, consider applying for an AI for Earth grant to support your compute requirements.

Imports and environment

In [12]:
# Standard packages
import tempfile
import warnings
import urllib
import shutil
import os

# Less standard, but still pip- or conda-installable
import matplotlib.pyplot as plt
import numpy as np
import rasterio
import re
import rtree
import shapely
import pickle

# pip install progressbar2, not progressbar
import progressbar

from geopy.geocoders import Nominatim
from rasterio.windows import Window 
from tqdm import tqdm

latest_wkid = 3857
crs = "EPSG:4326"

# Storage locations are documented at http://aka.ms/ai4edata-naip

# The(preferred) copy of NAIP in the West Europe Azure region
blob_root = 'https://naipeuwest.blob.core.windows.net/naip'

# NAIP is also available in the East US Azure region
# blob_root = 'https://naipblobs.blob.core.windows.net/naip'

index_files = ["tile_index.dat", "tile_index.idx", "tiles.p"]
index_blob_root = re.sub('/naip$','/naip-index/rtree/',blob_root)
temp_dir = os.path.join(tempfile.gettempdir(),'naip')
os.makedirs(temp_dir,exist_ok=True)

# Spatial index that maps lat/lon to NAIP tiles; we'll load this when we first 
# need to access it.
index = None

# URL where we've stashed a geojson file with the boundaries of Maryland.  Why do we
# need the boundaries of Maryland?  It's a surprise, you'll have to keep reading to find
# out.
maryland_boundary_url = 'https://ai4edatasetspublicassets.blob.core.windows.net/assets/maryland.json'

warnings.filterwarnings("ignore")
%matplotlib inline

Functions

In [6]:
class DownloadProgressBar():
    """
    https://stackoverflow.com/questions/37748105/how-to-use-progressbar-module-with-urlretrieve
    """
    
    def __init__(self):
        self.pbar = None

    def __call__(self, block_num, block_size, total_size):
        if not self.pbar:
            self.pbar = progressbar.ProgressBar(max_value=total_size)
            self.pbar.start()
            
        downloaded = block_num * block_size
        if downloaded < total_size:
            self.pbar.update(downloaded)
        else:
            self.pbar.finish()
            

class NAIPTileIndex:
    """
    Utility class for performing NAIP tile lookups by location.
    """
    
    tile_rtree = None
    tile_index = None
    base_path = None
    
    def __init__(self, base_path=None):
        
        if base_path is None:
            
            base_path = temp_dir
            os.makedirs(base_path,exist_ok=True)
            
            for file_path in index_files:
                download_url(index_blob_root + file_path, base_path + '/' + file_path,
                             progress_updater=DownloadProgressBar())
                
        self.base_path = base_path
        self.tile_rtree = rtree.index.Index(base_path + "/tile_index")
        self.tile_index = pickle.load(open(base_path  + "/tiles.p", "rb"))
      
    
    def lookup_tile(self, lat, lon):
        """"
        Given a lat/lon coordinate pair, return the list of NAIP tiles that contain
        that location.

        Returns a list of COG file paths.
        """

        point = shapely.geometry.Point(float(lon),float(lat))
        intersected_indices = list(self.tile_rtree.intersection(point.bounds))

        intersected_files = []
        tile_intersection = False

        for idx in intersected_indices:

            intersected_file = self.tile_index[idx][0]
            intersected_geom = self.tile_index[idx][1]
            if intersected_geom.contains(point):
                tile_intersection = True
                intersected_files.append(intersected_file)

        if not tile_intersection and len(intersected_indices) > 0:
            print('''Error: there are overlaps with tile index, 
                      but no tile completely contains selection''')   
            return None
        elif len(intersected_files) <= 0:
            print("No tile intersections")
            return None
        else:
            return intersected_files
        
            
def download_url(url, destination_filename=None, progress_updater=None, force_download=False):
    """
    Download a URL to a temporary file
    """
    
    # This is not intended to guarantee uniqueness, we just know it happens to guarantee
    # uniqueness for this application.
    if destination_filename is None:
        url_as_filename = url.replace('://', '_').replace('/', '_')    
        destination_filename = \
            os.path.join(temp_dir,url_as_filename)
    if (not force_download) and (os.path.isfile(destination_filename)):
        print('Bypassing download of already-downloaded file {}'.format(os.path.basename(url)))
        return destination_filename
    print('Downloading file {} to {}'.format(os.path.basename(url),destination_filename),end='')
    urllib.request.urlretrieve(url, destination_filename, progress_updater)  
    assert(os.path.isfile(destination_filename))
    nBytes = os.path.getsize(destination_filename)
    print('...done, {} bytes.'.format(nBytes))
    return destination_filename
    

def display_naip_tile(filename):
    """
    Display a NAIP tile using rasterio.
    """
    
    # NAIP tiles are enormous; downsize for plotting in this notebook
    dsfactor = 10
    
    with rasterio.open(filename) as raster:

        # NAIP imagery has four channels: R, G, B, IR
        #
        # Stack RGB channels into an image; we won't try to render the IR channel
        #
        # rasterio uses 1-based indexing for channels.
        h = int(raster.height/dsfactor)
        w = int(raster.width/dsfactor)
        print('Resampling to {},{}'.format(h,w))
        r = raster.read(1, out_shape=(1, h, w))
        g = raster.read(2, out_shape=(1, h, w))
        b = raster.read(3, out_shape=(1, h, w))        
    
    rgb = np.dstack((r,g,b))
    fig = plt.figure(figsize=(7.5, 7.5), dpi=100, edgecolor='k')
    plt.imshow(rgb)
    raster.close()
    
    
def get_coordinates_from_address(address):
    """
    Look up the lat/lon coordinates for an address.
    """
    
    geolocator = Nominatim(user_agent="NAIP")
    location = geolocator.geocode(address)
    print('Retrieving location for address:\n{}'.format(location.address))
    return location.latitude, location.longitude

Access and plot a NAIP tile by constructing a path

In [7]:
# Tiles are stored at:
#
# [blob root]/v002/[state]/[year]/[state]_[resolution]_[year]/[quadrangle]/filename

year = '2015'
state = 'al'
resolution = '100cm'
quadrangle = '30086'
filename = 'm_3008601_ne_16_1_20150804.tif'
tile_url = blob_root + '/v002/' + state + '/' + year + '/' + state + '_' + resolution + \
    '_' + year + '/' + quadrangle + '/' + filename

print(tile_url)

# Download the image
image_filename = download_url(tile_url,progress_updater=DownloadProgressBar())

# Plot the image
print('Reading file:\n{}'.format(os.path.basename(image_filename)))
assert os.path.isfile(image_filename)
display_naip_tile(image_filename)
https://naipeuwest.blob.core.windows.net/naip/v002/al/2015/al_100cm_2015/30086/m_3008601_ne_16_1_20150804.tif
Bypassing download of already-downloaded file m_3008601_ne_16_1_20150804.tif
Reading file:
https_naipeuwest.blob.core.windows.net_naip_v002_al_2015_al_100cm_2015_30086_m_3008601_ne_16_1_20150804.tif
Resampling to 753,657

Load the spatial index of NAIP tiles

In [14]:
if index is None:
    index = NAIPTileIndex()

Access and plot a NAIP tile based on a lat/lon coordinate pair

In [16]:
lat = 47.645950
lon = -122.136980

naip_files = index.lookup_tile(lat, lon)

print('\nList of available naip files for this location:\n')
for file in naip_files:
    print(file)
print(