Global topographic information from the NASADEM program.
NASADEM provides global topographic data at 1 arc-second (~30m) horizontal resolution, derived primarily from data captured via the Shuttle Radar Topography Mission (SRTM).
Storage resources
Data are stored in blobs in the West Europe Azure region, in the following blob container:
https://nasademeuwest.blob.core.windows.net/nasadem-nc
Within that container, data are organized according to:
v001/NASADEM_NC_[lat_tile][lon_tile].[extension]
lat_tile
is a character (‘n’ or ‘s’) plus a two-digit latitude value, derived from the latitude represented by each tile. To find the latitude code for a given latitude, perform a floor() operation, then take the absolute value, then prefix with ‘n’ for positive latitudes (including zero) and ‘s’ for negative latitudes, and zero-pad to two digits. For example, the latitude 35.3606 becomes ‘n35’.
long_tile
is a character (‘e’ or ‘w’) plus a three-digit longitude value, derived from the longitude represented by each tile. To find the longitude code for a given longitude, perform a floor() operation, then take the absolute value, then prefix with ‘e’ for positive latitudes (including zero) and ‘w’ for negative latitudes, and zero-pad to three digits. For example, the longitude 138.72 becomes ‘e138’.
For each tile, filenames with three extensions are present:
- 1.jpg (a preview image)
- nc (the data itself)
- nc.xml (tile creation metadata)
Images are stored in NetCDF format.
A complete Python example of accessing and plotting NASADEM data is available in the notebook provided under “data access”.
We also provide a read-only SAS (shared access signature) token to allow access to NASADEM data via, e.g., BlobFuse, which allows you to mount blob containers as drives:
?sv=2019-12-12&si=nasadem-ro&sr=c&sig=6rAbZIvIjQVTov2bGUpqH9T0fzTipRahkooOSf2XCuo%3D
Mounting instructions for Linux are here.
Large-scale processing is best performed in the East US Azure data center, where the images are stored. If you are using NASADEM data for environmental science applications, consider applying for an AI for Earth grant to support your compute requirements.
A copy of NASADEM is also available in the East US Azure region, and will be maintained there until at least the end of 2021, but we encourage users to migrate to the West Europe copy. The East US data is in the following container:
https://nasadem.blob.core.windows.net/nasadem-nc
Index
A full list of files is available here.
License and attribution information
NASADEM data may be used without restriction for any purpose whatsoever, commercial or otherwise, free of any royalties or other restrictions. No special permission or compensation is required, even for reselling the exact data, images or other derived products.
If possible, when using this data, please attribute to “Courtesy NASA/JPL-Caltech”.
Pretty picture
Topography in the Mount Fuji area.
Contact
For questions about this dataset, contact aiforearthdatasets@microsoft.com
.
Notices
MICROSOFT PROVIDES AZURE OPEN DATASETS ON AN “AS IS” BASIS. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, GUARANTEES OR CONDITIONS WITH RESPECT TO YOUR USE OF THE DATASETS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAW, MICROSOFT DISCLAIMS ALL LIABILITY FOR ANY DAMAGES OR LOSSES, INCLUDING DIRECT, CONSEQUENTIAL, SPECIAL, INDIRECT, INCIDENTAL OR PUNITIVE, RESULTING FROM YOUR USE OF THE DATASETS.
This dataset is provided under the original terms that Microsoft received source data. The dataset may include data sourced from Microsoft.
Access
Available in | When to use |
---|---|
Azure Notebooks | Quickly explore the dataset with Jupyter notebooks hosted on Azure or your local machine. |
Select your preferred service:
Azure Notebooks
# Mostly-standard imports
import os
import tempfile
import numpy as np
import shutil
import urllib
import math
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
# Less-common-but-still-pip-installable imports
from netCDF4 import Dataset
# pip install progressbar2, not progressbar
import progressbar
# Storage locations are documented at http://aka.ms/ai4edata-nasadem
nasadem_account_name = 'nasademeuwest'
nasadem_container_name = 'nasadem-nc'
nasadem_account_url = 'https://' + nasadem_account_name + '.blob.core.windows.net'
nasadem_blob_root = nasadem_account_url + '/' + nasadem_container_name + '/v001/'
# A full list of files is available at:
#
# https://nasademeuwest.blob.core.windows.net/nasadem-nc/v001/index/file_list.txt
nasadem_file_index_url = nasadem_blob_root + 'index/nasadem_file_list.txt'
nasadem_content_extension = '.nc'
nasadem_file_prefix = 'NASADEM_NC_'
# This will contain just the .nc files
nasadem_file_list = None
temp_dir = os.path.join(tempfile.gettempdir(),'nasadem')
os.makedirs(temp_dir,exist_ok=True)
%matplotlib inline
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()
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 lat_lon_to_nasadem_tile(lat,lon):
"""
Get the NASADEM file name for a specified latitude and longitude
"""
# A tile name looks like:
#
# NASADEM_NUMNC_n00e016.nc
#
# The translation from lat/lon to that string is represented nicely at:
#
# https://dwtkns.com/srtm30m/
# Force download of the file list
get_nasadem_file_list()
ns_token = 'n' if lat >=0 else 's'
ew_token = 'e' if lon >=0 else 'w'
lat_index = abs(math.floor(lat))
lon_index = abs(math.floor(lon))
lat_string = ns_token + '{:02d}'.format(lat_index)
lon_string = ew_token + '{:03d}'.format(lon_index)
filename = nasadem_file_prefix + lat_string + lon_string + \
nasadem_content_extension
if filename not in nasadem_file_list:
print('Lat/lon {},{} not available'.format(lat,lon))
filename = None
return filename
def get_nasadem_file_list():
"""
Retrieve the full list of NASADEM tiles
"""
global nasadem_file_list
if nasadem_file_list is None:
nasadem_file = download_url(nasadem_file_index_url)
with open(nasadem_file) as f:
nasadem_file_list = f.readlines()
nasadem_file_list = [f.strip() for f in nasadem_file_list]
nasadem_file_list = [f for f in nasadem_file_list if \
f.endswith(nasadem_content_extension)]
return nasadem_file_list
# Interesting places for looking at SRTM data
everest = [27.9881,86.9250]
seattle = [47.6062,-122.3321]
grand_canyon = [36.101690, -112.107676]
mount_fuji = [35.3606, 138.7274]
mont_blanc = [45.832778, 6.865000]
invalid = [-15.1,41]
tile_of_interest = mount_fuji
tile_name = lat_lon_to_nasadem_tile(tile_of_interest[0],tile_of_interest[1])
url = nasadem_blob_root + tile_name
fn = download_url(url,progress_updater=DownloadProgressBar())
fh = Dataset(fn, mode='r')
heights = fh['NASADEM_HGT'][:]
lons = fh.variables['lon'][:]
lats = fh.variables['lat'][:]
min_height = np.min(heights)
max_height = np.max(heights)
height_units = fh.variables['NASADEM_HGT'].units
fh.close()
print('Height ranges from {} {} to {} {}'.format(min_height,height_units,
max_height,height_units))
extent = [np.min(lons), np.max(lons), np.min(lats), np.max(lats)]
plt.imshow(heights,extent=extent)
plt.xlabel('Longitude')
plt.ylabel('Latitude')
cb = plt.colorbar()
cb.set_label('Height ({})'.format(height_units))
# To plot on a basemap, we'll want to downsample the data substantially
ds_factor = 10
# Show a little space around each edge on the basemap (this is in lat/lon units)
expansion_distance = 0.75
lon_0 = lons.mean()
lat_0 = lats.mean()
lons_ds = lons[::ds_factor]
lats_ds = lats[::ds_factor]
heights_ds = heights[::ds_factor,::ds_factor,]
# Convert to a 2D grid for plotting
lon_plot, lat_plot = np.meshgrid(lons_ds, lats_ds)
plt.figure(num=None, figsize=(8, 8), dpi=100)
m = Basemap(projection='stere',resolution='l',
lat_ts=lat_0,lat_0=lat_0,lon_0=lon_0,
llcrnrlon=extent[0]-expansion_distance,
llcrnrlat=extent[2]-expansion_distance,
urcrnrlon=extent[1]+expansion_distance,
urcrnrlat=extent[3]+expansion_distance)
xi, yi = m(lon_plot, lat_plot)
cs = m.pcolor(xi,yi,np.squeeze(heights_ds))
# Add grid lines
m.drawparallels(np.arange(-80., 81., 10.), labels=[1,0,0,0], fontsize=10)
m.drawmeridians(np.arange(-180., 181., 10.), labels=[0,0,0,1], fontsize=10)
# Add coastlines and state/country borders
# drawcoastlines() fails when no coastlines are visible
try:
m.drawcoastlines()
except:
pass
m.drawstates()
m.drawcountries()
cbar = m.colorbar(cs, location='bottom', pad="10%")
cbar.set_label(height_units)
plt.title('SRTM height')
plt.show()
shutil.rmtree(temp_dir)