탐색 건너뛰기

NAIP

AerialImagery AIforEarth USDA

NAIP(National Agricultural Imagery Program)의 항공 이미지입니다.

NAIP는 미국 전체의 고해상도 항공 이미지를 제공합니다. 이 프로그램은 USDA(미국 농무부) 내의 AFPO(Aerial Field Photography Office)에서 관리합니다. 2010년부터 현재까지 데이터를 사용할 수 있습니다.

스토리지 리소스

데이터는 미국 동부 데이터 센터에 있는 다음 Blob 컨테이너의 Azure Blob Storage에 클라우드 최적화 GeoTIFF 파일로 저장됩니다.

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

해당 컨테이너 내에서 데이터는 다음에 따라 정리됩니다.

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

…예:

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

이 필드에 대한 자세한 정보:

  • Year: 4자리 연도입니다. 이미지는 각 주에서 3~5년마다 수집됩니다(지정된 연도는 일부(전부는 아님) 주를 포함함). 예를 들어 앨라배마의 경우 2011년과 2013년 데이터는 있지만 2012년 데이터는 없고, 캘리포니아의 경우 2012년 데이터는 있지만 2011년 또는 2013년 데이터는 없습니다. Esri는 대화형 NAIP 연간 범위 맵에서 NAIP 범위에 대한 정보를 제공합니다.
  • State: 2자로 된 주 코드입니다.
  • Resolution: 이미지 해상도의 문자열 사양으로, 전체 NAIP 기록에서 다양합니다. 연도와 주에 따라 “050cm”, “060cm” 또는 “100cm”일 수 있습니다.
  • Quadrangle: 7.5분 x 7.5분 영역을 지정하는 USGS 사각형 식별자입니다.

파일은 확장명이 .tif인 클라우드 최적화 GeoTIFF 이미지로 저장됩니다. 이 파일은 USDA에서 제공한 원래 형식에서 생성되고 Esri에서 이 파일을 정리합니다.

각 이미지의 작은 미리 보기도 제공됩니다. 미리 보기를 검색하려면 “.tif”를 “.200.jpg”로 대체하세요. 예를 들어:

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

NAIP 이미지에 액세스하고 NAIP 이미지를 그리는 전체 Python 예제는 “데이터 액세스” 아래에서 제공되는 Notebook에서 사용할 수 있습니다.

또한 Blob 컨테이너를 드라이브로 탑재할 수 있게 해주는 BlobFuse 등을 통해 NAIP 데이터에 액세스할 수 있는 읽기 전용 SAS(공유 액세스 서명) 토큰을 제공합니다.

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

Linux용 탑재 지침은 여기에 있습니다.

NAIP 데이터는 수백 테라바이트를 소비할 수 있으므로 대규모 처리는 이미지가 저장된 미국 동부 Azure 데이터 센터에서 최상으로 수행됩니다. 환경 과학 응용 분야에 NAIP 데이터를 사용 중인 경우 컴퓨팅 요구 사항을 지원하려면 지구환경 AI(AI for Earth) 보조금 신청을 고려해 보세요.

인덱스

모든 NAIP 파일 목록은 다음 위치에서 압축 .csv 파일로 제공됩니다.

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

또한 Python 사용자가 공간 쿼리를 쉽게 할 수 있도록 rtree 개체도 유지 관리합니다. 자세한 내용은 샘플 Notebook을 참조하세요.

또한 여기에서 데이터를 찾아볼 수 있습니다.

.mrf 파일은 어디로 갔나요?

2020년 6월에 범위와 유지 관리성을 모두 개선하기 위해 전체 NAIP 아카이브를 업데이트했습니다. 또한 .mrf 형식에서 클라우드 최적화 GeoTIFF로 전환했으며, 경로 구조를 변경했습니다. .mrf 파일은 일시적으로 다른 컨테이너에서 계속 사용할 수 있습니다. .mrf 파일이 작업에 중요하고 액세스 권한이 필요한 경우 aiforearthdatasets@microsoft.com에 문의하세요.

멋진 사진


2017년 Microsoft’ Redmond Campus 근처 지역의 1m 해상도 이미지입니다.

연락처

이 데이터 세트에 대한 질문이 있는 경우 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 East US data center, so this notebook will run most efficiently on Azure compute located in East US. We recommend that substantial computation depending on NAIP data also be situated in East US. 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 [1]:
# Standard packages
import tempfile
import warnings
import urllib
import shutil
import os

# Workaround for a problem in older rasterio versions
os.environ["CURL_CA_BUNDLE"] = "/etc/ssl/certs/ca-certificates.crt" 

# Less standard, but still pip- or conda-installable
import matplotlib.pyplot as plt
import numpy as np
import rasterio
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
blob_root = 'https://naipblobs.blob.core.windows.net/naip'

index_files = ["tile_index.dat", "tile_index.idx", "tiles.p"]
index_blob_root = 'https://naipblobs.blob.core.windows.net/naip-index/rtree/'
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 [2]:
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 an array containing [mrf filename, idx filename, lrc filename].
        """

        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.
    
    For .mrf-formatted tiles (which span multiple files), 'filename' should refer to the 
    .mrf file.
    """
    
    # 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 [3]:
# 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://naipblobs.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_naipblobs.blob.core.windows.net_naip_v002_al_2015_al_100cm_2015_30086_m_3008601_ne_16_1_20150804.tif
Resampling to 753,657