Source code for poets.grid.grids

# Copyright (c) 2014, Vienna University of Technology (TU Wien), Department
# of Geodesy and Geoinformation (GEO).
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the Vienna University of Technology - Department of
#   Geodesy and Geoinformation nor the names of its contributors may be used to
#   endorse or promote products derived from this software without specific
#   prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VIENNA UNIVERSITY OF TECHNOLOGY,
# DEPARTMENT OF GEODESY AND GEOINFORMATION BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Author: Thomas Mistelbauer Thomas.Mistelbauer@geo.tuwien.ac.at
# Creation date: 2014-06-03


import pytesmo.grid.grids as grids
import numpy as np
import pandas as pd
import math
from shapely.geometry import Point
from poets.shape.shapes import Shape
from poets.image.imagefile import dateline_country


[docs]class RegularGrid(grids.BasicGrid): """Regular grid with coordinates as pixel center. Regular grid that just has lat, lon coordinates and can find the nearest neighbour. It can also yield the gpi, lat, lon information in order. Parameters ---------- lon : numpy.array longitudes of the points in the grid lat : numpy.array latitudes of the points in the grid gpis : numpy.array, optional if the gpi numbers are in a different order than the lon and lat arrays an array containing the gpi numbers can be given if no array is given here the lon lat arrays are given gpi numbers starting at 0 subset : numpy.array, optional if the active part of the array is only a subset of all the points then the subset array which is a index into lon and lat can be given here setup_kdTree : boolean, optional if set (default) then the kdTree for nearest neighbour search will be built on initialization shape : tuple, optional if given the grid can be reshaped into the given shape this indicates that it is a regular grid and fills the attributes self.londim and self.latdim which define the grid only be the meridian coordinates(self.londim) and the coordinates of the circles of latitude(self.latdim). The shape has to be given as (latdim, londim) It it is not given the shape is set to the length of the input lon and lat arrays. sp_res : int or float Spatial resolution of the grid. Attributes ---------- arrlon : numpy.array array of all longitudes of the grid arrlat : numpy.array array of all latitudes of the grid n_gpi : int number of gpis in the grid gpidirect : boolean if true the gpi number is equal to the index of arrlon and arrlat gpis : numpy.array gpi number for elements in arrlon and arrlat gpi[i] is located at arrlon[i],arrlat[i] subset : numpy.array if given then this contains the indices of a subset of the grid. This can be used if only a part of a grid is interesting for a application. e.g. land points, or only a specific country allpoints : boolean if False only a subset of the grid is active activearrlon : numpy.array array of longitudes that are active, is defined by arrlon[subset] if a subset is given otherwise equal to arrlon activearrlat : numpy.array array of latitudes that are active, is defined by arrlat[subset] if a subset is given otherwise equal to arrlat activegpis : numpy.array array of gpis that are active, is defined by gpis[subset] if a subset is given otherwise equal to gpis issplit : boolean if True then the array was split in n parts with the self.split function kdTree : object grid.nearest_neighbor.findGeoNN object for nearest neighbor search shape : tuple, optional if given during initialization then this is the shape the grid can be reshaped to this only makes sense for regular lat,lon grids latdim : numpy.array, optional if shape is given this attribute has contains all latitudes that make up the regular lat,lon grid londim : numpy.array, optional if shape is given this attribute has contains all longitudes that make up the regular lat,lon grid """ def __init__(self, sp_res, **kwargs): lonmin = -180. + (sp_res / 2.) latmin = -90. + (sp_res / 2.) londim = np.arange(lonmin, 180, sp_res) latdim = np.arange(latmin, 90, sp_res) lon, lat = np.meshgrid(londim, latdim) super(RegularGrid, self).__init__(lon.flatten(), lat.flatten(), shape=lon.shape, **kwargs)
[docs]class ShapeGrid(grids.BasicGrid): """Regular grid for a specific shape. Regular grid for spedific shape, that just has lat,lon coordinates and can find the nearest neighbour. It can also yield the gpi, lat, lon information in order. Parameters ---------- lon : numpy.array longitudes of the points in the grid lat : numpy.array latitudes of the points in the grid gpis : numpy.array, optional if the gpi numbers are in a different order than the lon and lat arrays an array containing the gpi numbers can be given if no array is given here the lon lat arrays are given gpi numbers starting at 0 subset : numpy.array, optional if the active part of the array is only a subset of all the points then the subset array which is a index into lon and lat can be given here setup_kdTree : boolean, optional if set (default) then the kdTree for nearest neighbour search will be built on initialization shape : tuple, optional if given the grid can be reshaped into the given shape this indicates that it is a regular grid and fills the attributes self.londim and self.latdim which define the grid only be the meridian coordinates(self.londim) and the coordinates of the circles of latitude(self.latdim). The shape has to be given as (latdim, londim) It it is not given the shape is set to the length of the input lon and lat arrays. region : str, optional Identifier of the region in the shapefile. If the default shapefile is used, this would be the FIPS country code. sp_res : float Spatial resolution of the grid. shapefile : str, optional Path to shape file, uses "world country admin boundary shapefile" by default. Attributes ---------- arrlon : numpy.array array of all longitudes of the grid arrlat : numpy.array array of all latitudes of the grid n_gpi : int number of gpis in the grid gpidirect : boolean if true the gpi number is equal to the index of arrlon and arrlat gpis : numpy.array gpi number for elements in arrlon and arrlat gpi[i] is located at arrlon[i],arrlat[i] subset : numpy.array if given then this contains the indices of a subset of the grid. This can be used if only a part of a grid is interesting for a application. e.g. land points, or only a specific country allpoints : boolean if False only a subset of the grid is active activearrlon : numpy.array array of longitudes that are active, is defined by arrlon[subset] if a subset is given otherwise equal to arrlon activearrlat : numpy.array array of latitudes that are active, is defined by arrlat[subset] if a subset is given otherwise equal to arrlat activegpis : numpy.array array of gpis that are active, is defined by gpis[subset] if a subset is given otherwise equal to gpis issplit : boolean if True then the array was split in n parts with the self.split function kdTree : object grid.nearest_neighbor.findGeoNN object for nearest neighbor search shape : tuple, optional if given during initialization then this is the shape the grid can be reshaped to this only makes sense for regular lat,lon grids latdim : numpy.array, optional if shape is given this attribute has contains all latitudes that make up the regular lat,lon grid londim : numpy.array, optional if shape is given this attribute has contains all longitudes that make up the regular lat,lon grid region : str Identifier of the region in the shapefile. If the default shapefile is used, this would be the FIPS country code. sp_res : float spatial resolution of the grid shp : poets.shape.shapes.Country Information about the country/region shape """ def __init__(self, region, sp_res, shapefile=None): self.country = region self.shp = Shape(region, shapefile=shapefile) # countries that cross the international dateline (maybe more!) if region in ['NZ', 'RS', 'US']: lonmin, lonmax = dateline_country(region) else: lonmin, lonmax = _minmaxcoord(self.shp.bbox[0], self.shp.bbox[2], sp_res) latmin, latmax = _minmaxcoord(self.shp.bbox[1], self.shp.bbox[3], sp_res) if region in ['NZ', 'US', 'RS']: lons1 = np.arange(lonmin, 180, sp_res) lons2 = (np.arange(-180, lonmax + sp_res, sp_res)) lons = np.empty(len(lons1) + len(lons2)) lons[0:len(lons1)] = lons1[:] lons[len(lons1):] = lons2[:] else: lons = np.arange(lonmin, lonmax + sp_res, sp_res) lats = np.arange(latmin, latmax + sp_res, sp_res) lon_new, lat_new = _remove_blank_frame(region, lons, lats, shapefile) lon, lat = np.meshgrid(lon_new, lat_new) super(ShapeGrid, self).__init__(lon.flatten(), lat.flatten(), shape=lon.shape)
[docs] def get_gridpoints(self): """Gets all points within a country shape. Removes all gridpoints that are within country bounding box, but outside of the country border. Returns ------- points : pd.DataFrame Dataframe containing all points with GPI as index, lat and lon as columns. """ box = self.get_bbox_grid_points(self.shp.bbox[1], self.shp.bbox[3], self.shp.bbox[0], self.shp.bbox[2]) poly = self.shp.polygon # MultiPolygon lons = [] lats = [] pts = [] for gpi in box: lon, lat = self.gpi2lonlat(gpi) if poly.contains(Point(lon, lat)): pts.append(gpi) lons.append(lon) lats.append(lat) points = pd.DataFrame({'lon': lons, 'lat': lats}, pts) return points
def _remove_blank_frame(region, lons, lats, shapefile=None): """Removes longitudes and latitudes without points in region shape. Parameters ---------- region : str FIPS country code (https://en.wikipedia.org/wiki/FIPS_country_code) lons : numpy.ndarray Array with longitudes. lats : numpy.ndarray Array with latitudes. Returns -------- lon_new : list of floats Updated list of longitudes. lat_new : list of floats Updated list of latitudes. """ shp = Shape(region, shapefile) poly = shp.polygon del_lons = [] del_lats = [] # left boundary check for i, x in enumerate(lons[:(lons.size / 2)]): checksum = 0 for y in lats: p = Point(x, y) if not p.within(poly): checksum += 1 if checksum == lats.size: del_lons.append(i) else: break # right boundary check for i, x in enumerate(lons[::-1][:(lons.size / 2)]): checksum = 0 for y in lats: p = Point(x, y) if not p.within(poly): checksum += 1 if checksum == lats.size: del_lons.append(lons.size - 1 - i) else: break # bottom boundary check for i, y in enumerate(lats[:(lats.size / 2)]): checksum = 0 for x in lons: p = Point(x, y) if not p.within(poly): checksum += 1 if checksum == lons.size: del_lats.append(i) else: break # top boundary check for i, y in enumerate(lats[::-1][:(lats.size / 2)]): checksum = 0 for x in lons: p = Point(x, y) if not p.within(poly): checksum += 1 if checksum == lons.size: del_lats.append(lats.size - 1 - i) else: break lon_new = lons.tolist() lat_new = lats.tolist() for i in del_lons: if lons[i] in lon_new: lon_new.remove(lons[i]) for i in del_lats: if lats[i] in lat_new: lat_new.remove(lats[i]) return lon_new, lat_new def _minmaxcoord(min_threshold, max_threshold, sp_res): """Gets min and max coordinates of a specific grid. Based on the frame of a global grid. Parameters ---------- min_threshold : float Minimum value of coordinate max_threshold : float Maximum value of coordinate sp_res : float or int Spatial resolution or the grid Returns ------- minval : float Updated minimum coordinate maxval : float Updated maximum coordinate """ res = float(sp_res) minval = int(math.ceil(min_threshold / res)) * res maxval = int(math.floor(max_threshold / res)) * res if minval != maxval: if minval - (res / 2) < min_threshold: minval += res / 2 else: minval -= res / 2 if maxval + (res / 2) > max_threshold: maxval -= res / 2 else: maxval += res / 2 return minval, maxval