Skip to content

Commit

Permalink
Merge pull request gee-community#342 from lopezvoliver/netcdf_to_ee
Browse files Browse the repository at this point in the history
Netcdf to ee
  • Loading branch information
giswqs authored Mar 6, 2021
2 parents cb54382 + 1ef42c2 commit 6691fd8
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,12 @@ To import a 2D or 3D numpy array to an ee.Image using a given base coordinate re
geemap.numpy_to_ee(np_array, crs, transform, transformWkt, band_names)
To import one or more variables from a netCDF file with a regular grid in EPSG:4326 to an ee.Image:

.. code:: python
geemap.netcdf_to_ee(nc_file, var_names, band_names, lon='lon', lat='lat')
To calculate zonal statistics:

.. code:: python
Expand Down
130 changes: 130 additions & 0 deletions examples/notebooks/netcdf_to_ee.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Uncomment the following line to install [geemap](https://geemap.org) if needed.\n",
"\n",
"You will also need to install the following package:\n",
"- `xarray`"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# !pip install geemap"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import geemap\n",
"import ee\n",
"ee.Initialize()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"if not os.path.exists('wind-global.nc'):\n",
" url = 'https://github.com/giswqs/geemap/blob/master/examples/data/wind-global.nc'\n",
" import requests\n",
" r = requests.get(url)\n",
" wind_data = r.content\n",
" with open('wind-global.nc', 'wb') as f:\n",
" f.write(wind_data)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "f0ec3a01364a4eb391e4e25c6fe2725b",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Map(center=[40, -100], controls=(WidgetControl(options=['position'], widget=HBox(children=(ToggleButton(value=…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Test with only one variable\n",
"img = geemap.netcdf_to_ee(nc_file=\"wind-global.nc\", var_names='u_wind')\n",
"import geemap.colormaps as cm\n",
"palette=cm.palettes.YlOrRd\n",
"Map = geemap.Map()\n",
"Map.addLayer(img, {'min':-20, 'max':25, 'palette':palette, 'opacity':0.6})\n",
"Map"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "cbe695f1593144d5a13bcfe52add9776",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Map(center=[40, -100], controls=(WidgetControl(options=['position'], widget=HBox(children=(ToggleButton(value=…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Test with two variables\n",
"img2 = geemap.netcdf_to_ee(nc_file=\"wind-global.nc\", var_names=['u_wind','v_wind'])\n",
"Map2 = geemap.Map()\n",
"Map2.addLayer(img2, {'bands':['u_wind'],'min':-20, 'max':25, 'palette':palette, 'opacity':0.6})\n",
"Map2"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "geemap",
"language": "python",
"name": "geemap"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
91 changes: 91 additions & 0 deletions geemap/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1773,6 +1773,97 @@ def get_image_collection_thumbnails(
print(e)


def netcdf_to_ee(nc_file, var_names, band_names=None, lon="lon", lat="lat"):
"""
Creates an ee.Image from netCDF variables band_names that are read from nc_file. Currently only supports variables in a regular longitude/latitude grid (EPSG:4326).
Args:
nc_file (str): the name of the netCDF file to read
var_names (str or list): the name(s) of the variable(s) to read
band_names (list, optional): if given, the bands are renamed to band_names. Defaults to the original var_names
lon (str, optional): the name of the longitude variable in the netCDF file. Defaults to "lon"
lat (str, optional): the name of the latitude variable in the netCDF file. Defaults to "lat"
Returns:
image: An ee.Image
"""
try:
import xarray as xr

except Exception:
raise ImportError(
"You need to install xarray first. See https://github.com/pydata/xarray"
)

import numpy as np

try:

if not isinstance(nc_file, str):
print("The input file must be a string.")
return
if band_names and not isinstance(band_names, (list,str)):
print("Band names must be a string or list.")
return
if not isinstance(lon, str) or not isinstance(lat, str):
print("The longitude and latitude variable names must be a string.")
return

ds = xr.open_dataset(nc_file)
data = ds[var_names]

lon_data = data[lon]
lat_data = data[lat]

dim_lon = np.unique(np.ediff1d(lon_data))
dim_lat = np.unique(np.ediff1d(lat_data))

if (len(dim_lon)!=1) or (len(dim_lat)!=1):
print("The netCDF file is not a regular longitude/latitude grid")
return

try:
data = data.to_array()
# ^ this is only needed (and works) if we have more than 1 variable
# axis_for_roll will be used in case we need to use np.roll
# and should be 1 for the case with more than 1 variable
axis_for_roll=1
except Exception:
axis_for_roll=0
# .to_array() does not work (and is not needed!) if there is only 1 variable
# in this case, the axis_for_roll needs to be 0

data_np = np.array(data)

do_transpose = True # To do: figure out if we need to tranpose the data or not
if do_transpose:
try:
data_np = np.transpose(data_np, (0,2,1))
except Exception:
data_np = np.transpose(data_np)

# Figure out if we need to roll the data or not
# (see https://github.com/giswqs/geemap/issues/285#issuecomment-791385176)
if(np.max(lon_data)>180):
data_np = np.roll(data_np, 180, axis = axis_for_roll)
west_lon = lon_data[0]-180
else:
west_lon = lon_data[0]

transform = [dim_lon[0], 0, float(west_lon), 0, dim_lat[0], float(lat_data[0])]

if band_names is None:
band_names = var_names

image = numpy_to_ee(data_np, 'EPSG:4326', transform=transform, band_names=band_names)

return image

except Exception as e:
print(e)


def numpy_to_ee(np_array, crs=None, transform=None, transformWkt=None, band_names=None):
"""
Creates an ee.Image from a 3D numpy array where each 2D numpy slice is added to a band, and a geospatial transform that indicates where to put the data. If the np_array is already 2D only, then it is only a one-band image.
Expand Down

0 comments on commit 6691fd8

Please sign in to comment.