Module pycurious.mapping
The pycurious.mapping
module of PyCurious contains various functions to help
manipulate geospatial data into common formats. It handles commonly encounted
operations, such as:
- Gridding scattered data points
- Converting between coordinate reference systems (CRS)
- Importing and exporting GeoTiff files
It requires some additional dependencies:
matplotlib
- for plottingpyproj
- for transforming between different CRScartopy
- for generating maps
Beware that most global data are georeferenced in WGS84 (EPSG: 4326). The radial power spectrum must be in rad/km, which requires a transformation from longitude / latitude to a local projection in eastings / northings.
For example, EMAG2 is a global compilation of the magnetic anomaly georeferenced in WGS84 longitude / latitude. This will need to be projected in a local CRS to use with PyCurious. If, for example, we are interested in a region across Ireland we could use the IRENET95 local CRS (EPSG: 2157),
transform_coordinates(lons, lats, epsg_in=4326, epsg_out=2157)
which would return a list of eastings and northings in IRENET95 projection.
Source code
# Copyright 2018-2019 Ben Mather, Robert Delhaye
#
# This file is part of PyCurious.
#
# PyCurious is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or any later version.
#
# PyCurious is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PyCurious. If not, see <http://www.gnu.org/licenses/>.
"""
The `pycurious.mapping` module of PyCurious contains various functions to help
manipulate geospatial data into common formats. It handles commonly encounted
operations, such as:
- Gridding scattered data points
- Converting between coordinate reference systems (CRS)
- Importing and exporting GeoTiff files
It requires some **additional dependencies**:
- [`matplotlib`](https://matplotlib.org/) - for plotting
- [`pyproj`](https://github.com/jswhit/pyproj) - for transforming between different CRS
- [`cartopy`](https://scitools.org.uk/cartopy/docs/latest/) - for generating maps
Beware that most global data are georeferenced in WGS84 (EPSG: 4326).
The radial power spectrum must be in rad/km, which requires a transformation
from longitude / latitude to a local projection in eastings / northings.
For example, EMAG2 is a global compilation of the magnetic anomaly
georeferenced in WGS84 longitude / latitude. This will need to be projected
in a local CRS to use with PyCurious. If, for example, we are interested in a
region across Ireland we could use the IRENET95 local CRS (EPSG: 2157),
```python
transform_coordinates(lons, lats, epsg_in=4326, epsg_out=2157)
```
which would return a list of eastings and northings in IRENET95 projection.
"""
# -*- coding: utf-8 -*-
import numpy as np
try:
range = xrange
except:
pass
def transform_coordinates(x, y, epsg_in, epsg_out):
"""
Transform between any coordinate system.
**Requires `pyproj`** - install using pip.
Args:
x : float / 1D array
x coordinates (may be in degrees or metres/eastings)
y : float / 1D array
y coordinates (may be in degrees or metres/northings)
epsg_in : int
CRS of x and y coordinates
epsg_out : int
CRS of output
Returns:
x_out : float / list of floats
x coordinates projected in `epsg_out`
y_out : float / list of floats
y coordinates projected in `epsg_out`
"""
import pyproj
proj_in = pyproj.CRS("EPSG:" + str(epsg_in))
proj_out = pyproj.CRS("EPSG:" + str(epsg_out))
transformer = pyproj.Transformer.from_crs(proj_in, proj_out, always_xy=True)
return transformer.transform(x,y)
def convert_extent(extent_in, epsg_in, epsg_out):
"""
Transform extent from epsg_in to epsg_out
Args:
extent_in : tuple
bounding box [minX, maxX, minY, maxY]
epsg_in : int
CRS of extent
epsg_out : int
CRS of output
Returns:
extent_out : tuple
bounding box in new CRS
"""
xmin, xmax, ymin, ymax = extent_in
xi = [xmin, xmin, xmax, xmax]
yi = [ymin, ymax, ymin, ymax]
xo, yo = transform_coordinates(xi, yi, epsg_in, epsg_out)
extent_out = [min(xo), max(xo), min(yo), max(yo)]
return extent_out
def trim(coords, data, extent, buffer_amount=0.0):
"""
Trim a smaller section of a large dataset taking into
consideration transformations into various coordinate
reference systems (CRS).
Args:
coords : array shape (n,2)
geographical / projected coordinates
data : array shape (n,)
values corresponding to coordinates
extent : tuple
bounding box to trim data
buffer : float
amount of buffer to include (default=0.0)
Returns:
coords_trim : array shape (l,2)
trimmed coordinates
data_trim : array shape (l,2)
trimmed data array
"""
xmin, xmax, ymin, ymax = extent
# Extract only the data within the extent
data_mask = np.zeros(data.shape[0], dtype=bool)
# Add a 1 percent buffer zone
x_buffer = buffer_amount * (xmax - xmin)
y_buffer = buffer_amount * (ymax - ymin)
data_mask += coords[:, 0] < xmin - x_buffer
data_mask += coords[:, 0] > xmax + x_buffer
data_mask += coords[:, 1] < ymin - y_buffer
data_mask += coords[:, 1] > ymax + y_buffer
data_mask = np.invert(data_mask)
data_trim = data[data_mask]
coords_trim = coords[data_mask]
return coords_trim, data_trim
def grid(coords, data, extent, shape=None, epsg_in=None, epsg_out=None, **kwargs):
"""
Grid a smaller section of a large dataset taking into
consideration transformations into various coordinate
reference systems (CRS).
**Requires `scipy.interpolate.griddata`**
Args:
coords : array shape (n,2)
geographical coordinates
data : array shape (n,)
values corresponding to coordinates
extent : tuple
bounding box in espg_out coordinates
shape : tuple (nrows,ncols)
size of the box, if None, shape is estimated from coords spacing
epsg_in : int
CRS of data (if transformation is required)
epsg_out : int
CRS of grid (if transformation is required)
kwargs : keyword arguments
keyword arguments to pass to griddata from
`scipy.interpolate.griddata`
Returns:
grid : array shape (nrows, ncols)
rectangular section of data bounded by extent
"""
from scipy.interpolate import griddata
xmin, xmax, ymin, ymax = extent
if epsg_in is not None:
xt, yt = transform_coordinates(
np.array([xmin, xmin, xmax, xmax]),
np.array([ymin, ymax, ymin, ymax]),
epsg_out,
epsg_in,
)
# find the coordinates that will completely
# engulf the extent
xtmin, xtmax = min(xt), max(xt)
ytmin, ytmax = min(yt), max(yt)
else:
xtmin, xtmax = xmin, xmax
ytmin, ytmax = ymin, ymax
xtextent = [xtmin, xtmax, ytmin, ytmax]
# trim data - buffer = 5%
coords_trim, data_trim = trim(coords, data, xtextent, 0.05)
if epsg_in is not None:
# convert back to output CRS
xtrim, ytrim = transform_coordinates(
coords_trim[:, 0], coords_trim[:, 1], epsg_in, epsg_out
)
coords_trim = np.column_stack([xtrim, ytrim])
if shape == None:
# estimate based on the data spacing
xunique = np.unique(coords_trim[:, 0])
yunique = np.unique(coords_trim[:, 1])
dx = np.diff(xunique).mean()
dy = np.diff(yunique).mean()
nc = int((xtmax - xtmin) / dx)
nr = int((ytmax - ytmin) / dy)
print(
"using nrows={}, ncols={} with cell spacing of {}".format(nr, nc, (dy, dx))
)
else:
nr, nc = shape
# interpolate
xcoords = np.linspace(xmin, xmax, nc)
ycoords = np.linspace(ymin, ymax, nr)
xq, yq = np.meshgrid(xcoords, ycoords)
vq = griddata(coords_trim, data_trim, (xq, yq), **kwargs)
return vq
def ungrid(grid, extent, coordinates, **kwargs):
"""
Maps the value at unstructured coordinates from a regular 2D grid.
Uses the `scipy.ndimage.map_coordinates` function with cubic interpolation
Args:
grid : array shape (ny,nx)
regularly spaced grid
extent : tuple
bounding box of `grid` e.g. [xmin,xmax,ymin,ymax]
coordinates : array shape (n,2)
coordinates (x,y) to interpolate `grid`
kwargs : keyword arguments
keyword arguments to pass to `map_coordinates`
Returns:
grid_interp : array shape (n,)
interpolated values at coordinates
"""
from scipy.ndimage import map_coordinates
ny, nx = grid.shape
xmin, xmax, ymin, ymax = extent
# normalise coordinates within extent
icoords = coordinates.copy()
icoords[:,0] -= xmin
icoords[:,1] -= ymin
icoords[:,0] /= (xmax - xmin)
icoords[:,1] /= (ymax - ymin)
# icoords now somewhere within range [0, 1]
# project coordinates to the number of grid indices
icoords[:,0] *= nx - 1
icoords[:,1] *= ny - 1
# now interpolate
return map_coordinates(grid, icoords.T, **kwargs)
def import_geotiff(file_path):
"""
Import a GeoTIFF to a numpy array and prints
information of the Coordinate Reference System (CRS).
**Requires `osgeo`.**
Args:
file_path : str
path to the GeoTIFF
Returns:
data : 2D array
extent : tuple
bounding box in the projection of the GeoTIFF
e.g. [xmin, xmax, ymin, ymax]
"""
from osgeo import gdal, osr
gtiff = gdal.Open(file_path)
data = gtiff.ReadAsArray()
gt = gtiff.GetGeoTransform()
gtproj = gtiff.GetProjection()
inproj = osr.SpatialReference()
inproj.ImportFromWkt(gtproj)
gtextent = (
gt[0],
gt[0] + gtiff.RasterXSize * gt[1],
gt[3],
gt[3] + gtiff.RasterYSize * gt[5],
)
# print projection information
print(inproj)
# this closes the geotiff
gtiff = None
return data, gtextent
def export_geotiff(file_path, array, extent, epsg):
"""
Export a GeoTIFF from a numpy array projected in a
predefined Coordinate Reference System (CRS).
**Requires `osgeo`.**
Args:
file_path : str
path to write the GeoTIFF
array: 2D array
array to save to GeoTiff
extent : tuple
bounding box in the projection of the GeoTIFF
e.g. [xmin, xmax, ymin, ymax]
epsg : int
CRS of the GeoTIFF
e.g. 4326 for WGS84
"""
from osgeo import gdal, osr
# import ogr, gdal, osr, os
cols = array.shape[1]
rows = array.shape[0]
xmin, xmax, ymin, ymax = extent
spacingX = (xmax - xmin) / cols
spacingY = (ymax - ymin) / rows
driver = gdal.GetDriverByName("GTiff")
outRaster = driver.Create(file_path, cols, rows, 1, gdal.GDT_Float64)
outRaster.SetGeoTransform((xmin, spacingX, 0, ymin, 0, spacingY))
outband = outRaster.GetRasterBand(1)
outband.WriteArray(array)
outRasterSRS = osr.SpatialReference()
outRasterSRS.ImportFromEPSG(epsg)
outRaster.SetProjection(outRasterSRS.ExportToWkt())
outband.FlushCache()
return
def export_netcdf4(file_path, array, extent):
"""
Export a netCDF4 file from a numpy array
over a user-defined extent.
**Requires `netcdf4`.**
`pip install netcdf4`
Args:
file_path : str
path to write the netCDF4
array: 2D array
array to save to netCDF4
extent : tuple
bounding box in the projection of the netCDF4 file
e.g. [xmin, xmax, ymin, ymax]
"""
import netCDF4
ny, nx = array.shape
xmin, xmax, ymin, ymax = extent
with netCDF4.Dataset(str(file_path), 'w') as cdf:
cdf.createDimension('x', nx)
cdf.createDimension('y', ny)
cdf_x = cdf.createVariable('x', np.float, ('x',), zlib=True)
cdf_y = cdf.createVariable('y', np.float, ('y',), zlib=True)
cdf_x[:] = np.linspace(xmin, xmax, nx)
cdf_y[:] = np.linspace(ymin, ymax, ny)
cdf_data = cdf.createVariable('z', np.float, ('y','x'), zlib=True)
cdf_data[:,:] = array
def import_netcdf4(file_path):
"""
Import a netCDF4 file from a numpy array
over a user-defined extent.
**Requires `netcdf4`.**
`pip install netcdf4`
Args:
file_path : str
path to write the netCDF4
Returns:
array : ndarray
numpy array containing the gridded netCDF4 data
extent : tuple
bounding box in the projection of the netCDF4 file
e.g. [xmin, xmax, ymin, ymax]
Note:
The layout of the netCDF4 file must follow the standard
naming convention such as 'lon', 'lat', 'z', 'data', etc
otherwise this import function will not work as intended.
"""
import netCDF4
with netCDF4.Dataset(str(file_path), 'r') as cdf:
try:
lons = cdf['lon']
lats = cdf['lat']
except:
lons = cdf['x']
lats = cdf['y']
else:
raise ValueError(print(cdf))
try:
data = cdf['z'][:]
except:
data = cdf['data'][:]
else:
raise ValueError(print(cdf))
extent = [lons.min(), lons.max(), lats.min(), lats.max()]
return data, extent
Functions
def convert_extent(extent_in, epsg_in, epsg_out)
-
Transform extent from epsg_in to epsg_out
Args
extent_in
:tuple
- bounding box [minX, maxX, minY, maxY]
epsg_in
:int
- CRS of extent
epsg_out
:int
- CRS of output
Returns
extent_out
:tuple
- bounding box in new CRS
Source code
def convert_extent(extent_in, epsg_in, epsg_out): """ Transform extent from epsg_in to epsg_out Args: extent_in : tuple bounding box [minX, maxX, minY, maxY] epsg_in : int CRS of extent epsg_out : int CRS of output Returns: extent_out : tuple bounding box in new CRS """ xmin, xmax, ymin, ymax = extent_in xi = [xmin, xmin, xmax, xmax] yi = [ymin, ymax, ymin, ymax] xo, yo = transform_coordinates(xi, yi, epsg_in, epsg_out) extent_out = [min(xo), max(xo), min(yo), max(yo)] return extent_out
def export_geotiff(file_path, array, extent, epsg)
-
Export a GeoTIFF from a numpy array projected in a predefined Coordinate Reference System (CRS).
Requires
osgeo
.Args
file_path
:str
- path to write the GeoTIFF
array
- 2D array array to save to GeoTiff
extent
:tuple
- bounding box in the projection of the GeoTIFF e.g. [xmin, xmax, ymin, ymax]
epsg
:int
- CRS of the GeoTIFF e.g. 4326 for WGS84
Source code
def export_geotiff(file_path, array, extent, epsg): """ Export a GeoTIFF from a numpy array projected in a predefined Coordinate Reference System (CRS). **Requires `osgeo`.** Args: file_path : str path to write the GeoTIFF array: 2D array array to save to GeoTiff extent : tuple bounding box in the projection of the GeoTIFF e.g. [xmin, xmax, ymin, ymax] epsg : int CRS of the GeoTIFF e.g. 4326 for WGS84 """ from osgeo import gdal, osr # import ogr, gdal, osr, os cols = array.shape[1] rows = array.shape[0] xmin, xmax, ymin, ymax = extent spacingX = (xmax - xmin) / cols spacingY = (ymax - ymin) / rows driver = gdal.GetDriverByName("GTiff") outRaster = driver.Create(file_path, cols, rows, 1, gdal.GDT_Float64) outRaster.SetGeoTransform((xmin, spacingX, 0, ymin, 0, spacingY)) outband = outRaster.GetRasterBand(1) outband.WriteArray(array) outRasterSRS = osr.SpatialReference() outRasterSRS.ImportFromEPSG(epsg) outRaster.SetProjection(outRasterSRS.ExportToWkt()) outband.FlushCache() return
def export_netcdf4(file_path, array, extent)
-
Export a netCDF4 file from a numpy array over a user-defined extent.
Requires
netcdf4
.pip install netcdf4
Args
file_path
:str
- path to write the netCDF4
array
- 2D array array to save to netCDF4
extent
:tuple
- bounding box in the projection of the netCDF4 file e.g. [xmin, xmax, ymin, ymax]
Source code
def export_netcdf4(file_path, array, extent): """ Export a netCDF4 file from a numpy array over a user-defined extent. **Requires `netcdf4`.** `pip install netcdf4` Args: file_path : str path to write the netCDF4 array: 2D array array to save to netCDF4 extent : tuple bounding box in the projection of the netCDF4 file e.g. [xmin, xmax, ymin, ymax] """ import netCDF4 ny, nx = array.shape xmin, xmax, ymin, ymax = extent with netCDF4.Dataset(str(file_path), 'w') as cdf: cdf.createDimension('x', nx) cdf.createDimension('y', ny) cdf_x = cdf.createVariable('x', np.float, ('x',), zlib=True) cdf_y = cdf.createVariable('y', np.float, ('y',), zlib=True) cdf_x[:] = np.linspace(xmin, xmax, nx) cdf_y[:] = np.linspace(ymin, ymax, ny) cdf_data = cdf.createVariable('z', np.float, ('y','x'), zlib=True) cdf_data[:,:] = array
def grid(coords, data, extent, shape=None, epsg_in=None, epsg_out=None, **kwargs)
-
Grid a smaller section of a large dataset taking into consideration transformations into various coordinate reference systems (CRS).
Requires
scipy.interpolate.griddata
Args
coords
:array
shape
(n
,2
)- geographical coordinates
data
:array
shape
(n
,)- values corresponding to coordinates
extent
:tuple
- bounding box in espg_out coordinates
shape
:tuple
(nrows
,ncols
)- size of the box, if None, shape is estimated from coords spacing
epsg_in
:int
- CRS of data (if transformation is required)
epsg_out
:int
- CRS of grid (if transformation is required)
kwargs
:keyword
arguments
keyword arguments to pass to griddata from
scipy.interpolate.griddata
Returns
grid()
:array
shape
(nrows
,ncols
)- rectangular section of data bounded by extent
Source code
def grid(coords, data, extent, shape=None, epsg_in=None, epsg_out=None, **kwargs): """ Grid a smaller section of a large dataset taking into consideration transformations into various coordinate reference systems (CRS). **Requires `scipy.interpolate.griddata`** Args: coords : array shape (n,2) geographical coordinates data : array shape (n,) values corresponding to coordinates extent : tuple bounding box in espg_out coordinates shape : tuple (nrows,ncols) size of the box, if None, shape is estimated from coords spacing epsg_in : int CRS of data (if transformation is required) epsg_out : int CRS of grid (if transformation is required) kwargs : keyword arguments keyword arguments to pass to griddata from `scipy.interpolate.griddata` Returns: grid : array shape (nrows, ncols) rectangular section of data bounded by extent """ from scipy.interpolate import griddata xmin, xmax, ymin, ymax = extent if epsg_in is not None: xt, yt = transform_coordinates( np.array([xmin, xmin, xmax, xmax]), np.array([ymin, ymax, ymin, ymax]), epsg_out, epsg_in, ) # find the coordinates that will completely # engulf the extent xtmin, xtmax = min(xt), max(xt) ytmin, ytmax = min(yt), max(yt) else: xtmin, xtmax = xmin, xmax ytmin, ytmax = ymin, ymax xtextent = [xtmin, xtmax, ytmin, ytmax] # trim data - buffer = 5% coords_trim, data_trim = trim(coords, data, xtextent, 0.05) if epsg_in is not None: # convert back to output CRS xtrim, ytrim = transform_coordinates( coords_trim[:, 0], coords_trim[:, 1], epsg_in, epsg_out ) coords_trim = np.column_stack([xtrim, ytrim]) if shape == None: # estimate based on the data spacing xunique = np.unique(coords_trim[:, 0]) yunique = np.unique(coords_trim[:, 1]) dx = np.diff(xunique).mean() dy = np.diff(yunique).mean() nc = int((xtmax - xtmin) / dx) nr = int((ytmax - ytmin) / dy) print( "using nrows={}, ncols={} with cell spacing of {}".format(nr, nc, (dy, dx)) ) else: nr, nc = shape # interpolate xcoords = np.linspace(xmin, xmax, nc) ycoords = np.linspace(ymin, ymax, nr) xq, yq = np.meshgrid(xcoords, ycoords) vq = griddata(coords_trim, data_trim, (xq, yq), **kwargs) return vq
def import_geotiff(file_path)
-
Import a GeoTIFF to a numpy array and prints information of the Coordinate Reference System (CRS).
Requires
osgeo
.Args
file_path
:str
- path to the GeoTIFF
Returns
data
:2D
array
extent
:tuple
- bounding box in the projection of the GeoTIFF e.g. [xmin, xmax, ymin, ymax]
Source code
def import_geotiff(file_path): """ Import a GeoTIFF to a numpy array and prints information of the Coordinate Reference System (CRS). **Requires `osgeo`.** Args: file_path : str path to the GeoTIFF Returns: data : 2D array extent : tuple bounding box in the projection of the GeoTIFF e.g. [xmin, xmax, ymin, ymax] """ from osgeo import gdal, osr gtiff = gdal.Open(file_path) data = gtiff.ReadAsArray() gt = gtiff.GetGeoTransform() gtproj = gtiff.GetProjection() inproj = osr.SpatialReference() inproj.ImportFromWkt(gtproj) gtextent = ( gt[0], gt[0] + gtiff.RasterXSize * gt[1], gt[3], gt[3] + gtiff.RasterYSize * gt[5], ) # print projection information print(inproj) # this closes the geotiff gtiff = None return data, gtextent
def import_netcdf4(file_path)
-
Import a netCDF4 file from a numpy array over a user-defined extent.
Requires
netcdf4
.pip install netcdf4
Args
file_path
:str
- path to write the netCDF4
Returns
array
:ndarray
- numpy array containing the gridded netCDF4 data
extent
:tuple
- bounding box in the projection of the netCDF4 file e.g. [xmin, xmax, ymin, ymax]
Note
The layout of the netCDF4 file must follow the standard naming convention such as 'lon', 'lat', 'z', 'data', etc otherwise this import function will not work as intended.
Source code
def import_netcdf4(file_path): """ Import a netCDF4 file from a numpy array over a user-defined extent. **Requires `netcdf4`.** `pip install netcdf4` Args: file_path : str path to write the netCDF4 Returns: array : ndarray numpy array containing the gridded netCDF4 data extent : tuple bounding box in the projection of the netCDF4 file e.g. [xmin, xmax, ymin, ymax] Note: The layout of the netCDF4 file must follow the standard naming convention such as 'lon', 'lat', 'z', 'data', etc otherwise this import function will not work as intended. """ import netCDF4 with netCDF4.Dataset(str(file_path), 'r') as cdf: try: lons = cdf['lon'] lats = cdf['lat'] except: lons = cdf['x'] lats = cdf['y'] else: raise ValueError(print(cdf)) try: data = cdf['z'][:] except: data = cdf['data'][:] else: raise ValueError(print(cdf)) extent = [lons.min(), lons.max(), lats.min(), lats.max()] return data, extent
def transform_coordinates(x, y, epsg_in, epsg_out)
-
Transform between any coordinate system.
Requires
pyproj
- install using pip.Args
x
:float
/1D
array
- x coordinates (may be in degrees or metres/eastings)
y
:float
/1D
array
- y coordinates (may be in degrees or metres/northings)
epsg_in
:int
- CRS of x and y coordinates
epsg_out
:int
- CRS of output
Returns
x_out
:float
/list
offloats
- x coordinates projected in
epsg_out
y_out
:float
/list
offloats
- y coordinates projected in
epsg_out
Source code
def transform_coordinates(x, y, epsg_in, epsg_out): """ Transform between any coordinate system. **Requires `pyproj`** - install using pip. Args: x : float / 1D array x coordinates (may be in degrees or metres/eastings) y : float / 1D array y coordinates (may be in degrees or metres/northings) epsg_in : int CRS of x and y coordinates epsg_out : int CRS of output Returns: x_out : float / list of floats x coordinates projected in `epsg_out` y_out : float / list of floats y coordinates projected in `epsg_out` """ import pyproj proj_in = pyproj.CRS("EPSG:" + str(epsg_in)) proj_out = pyproj.CRS("EPSG:" + str(epsg_out)) transformer = pyproj.Transformer.from_crs(proj_in, proj_out, always_xy=True) return transformer.transform(x,y)
def trim(coords, data, extent, buffer_amount=0.0)
-
Trim a smaller section of a large dataset taking into consideration transformations into various coordinate reference systems (CRS).
Args
coords
:array
shape
(n
,2
)- geographical / projected coordinates
data
:array
shape
(n
,)- values corresponding to coordinates
extent
:tuple
- bounding box to trim data
buffer
:float
- amount of buffer to include (default=0.0)
Returns
coords_trim
:array
shape
(l
,2
)- trimmed coordinates
data_trim
:array
shape
(l
,2
)- trimmed data array
Source code
def trim(coords, data, extent, buffer_amount=0.0): """ Trim a smaller section of a large dataset taking into consideration transformations into various coordinate reference systems (CRS). Args: coords : array shape (n,2) geographical / projected coordinates data : array shape (n,) values corresponding to coordinates extent : tuple bounding box to trim data buffer : float amount of buffer to include (default=0.0) Returns: coords_trim : array shape (l,2) trimmed coordinates data_trim : array shape (l,2) trimmed data array """ xmin, xmax, ymin, ymax = extent # Extract only the data within the extent data_mask = np.zeros(data.shape[0], dtype=bool) # Add a 1 percent buffer zone x_buffer = buffer_amount * (xmax - xmin) y_buffer = buffer_amount * (ymax - ymin) data_mask += coords[:, 0] < xmin - x_buffer data_mask += coords[:, 0] > xmax + x_buffer data_mask += coords[:, 1] < ymin - y_buffer data_mask += coords[:, 1] > ymax + y_buffer data_mask = np.invert(data_mask) data_trim = data[data_mask] coords_trim = coords[data_mask] return coords_trim, data_trim
def ungrid(grid, extent, coordinates, **kwargs)
-
Maps the value at unstructured coordinates from a regular 2D grid. Uses the
scipy.ndimage.map_coordinates
function with cubic interpolationArgs
grid()
:array
shape
(ny
,nx
)- regularly spaced grid
extent
:tuple
- bounding box of
grid()
e.g. [xmin,xmax,ymin,ymax] coordinates
:array
shape
(n
,2
)- coordinates (x,y) to interpolate
grid()
kwargs
:keyword
arguments
- keyword arguments to pass to
map_coordinates
Returns
grid_interp
:array
shape
(n
,)- interpolated values at coordinates
Source code
def ungrid(grid, extent, coordinates, **kwargs): """ Maps the value at unstructured coordinates from a regular 2D grid. Uses the `scipy.ndimage.map_coordinates` function with cubic interpolation Args: grid : array shape (ny,nx) regularly spaced grid extent : tuple bounding box of `grid` e.g. [xmin,xmax,ymin,ymax] coordinates : array shape (n,2) coordinates (x,y) to interpolate `grid` kwargs : keyword arguments keyword arguments to pass to `map_coordinates` Returns: grid_interp : array shape (n,) interpolated values at coordinates """ from scipy.ndimage import map_coordinates ny, nx = grid.shape xmin, xmax, ymin, ymax = extent # normalise coordinates within extent icoords = coordinates.copy() icoords[:,0] -= xmin icoords[:,1] -= ymin icoords[:,0] /= (xmax - xmin) icoords[:,1] /= (ymax - ymin) # icoords now somewhere within range [0, 1] # project coordinates to the number of grid indices icoords[:,0] *= nx - 1 icoords[:,1] *= ny - 1 # now interpolate return map_coordinates(grid, icoords.T, **kwargs)