Module geodata_harvester.spatial

Utility functions for for spatial processing.

–Function List, in order of appearence–

_points_in_circle(internal): Return all points whose indices are within a given circle. _coreg_buffer(internal): Queries values of a raster around a point buffer region. raster_buffer: Given a longitude,latitude point, a raster file, and a buffer region, find the values of all points in circular buffer. _get_features(internal): Parse features from GeoDataFrame format to Rasterio format _coreg_polygon(internal): Crops a raster to a polygon shape. raster_polygon_buffer: Given list of longitudes and latitudes defining a polygon, crop raster file, return the values of all points in the polygon.

Expand source code
#!/bin/python
"""

Utility functions for for spatial processing.

--Function List, in order of appearence--

_points_in_circle(internal): Return all points whose indices are within a given
    circle.
_coreg_buffer(internal): Queries values of a raster around a point buffer
    region.
raster_buffer: Given a longitude,latitude point, a raster file, and a buffer
    region, find the values of all points in circular buffer.
_get_features(internal): Parse features from GeoDataFrame format to Rasterio
    format
_coreg_polygon(internal): Crops a raster to a polygon shape.
raster_polygon_buffer: Given list of longitudes and latitudes defining a
    polygon, crop raster file, return the values of all points in the polygon.
"""

from glob import glob
import os
import json

import rasterio
from rasterio.mask import mask
from rasterio.warp import calculate_default_transform, reproject, Resampling
from rasterio.plot import show

import numpy as np
import pandas as pd
import geopandas as gpd

from pyproj import CRS
from pathlib import Path

import matplotlib.pyplot as plt
from matplotlib.ticker import FormatStrFormatter

from numba import jit

from geodata_harvester import utils

from shapely.geometry import Polygon
from fiona.crs import from_epsg
import json


@jit(nopython=True)
def _points_in_circle(i0, j0, r, xlen, ylen):
    """
    A generator to return all points whose indices are within a given circle.
    http://stackoverflow.com/a/2774284
    Warning: If a point is near the the edges of the raster it will not loop
    around to the other side of the raster!
    We yield indexs of points, so this function may be sped up without the
    need for passing data matiricies back and forth.

    INPUTS
    i0: x index column locator centre point
    j0: y index row locator centre point
    r: radius of circle in pixel/index units
    xlen: no. columns of data array.
    ylen: no. rows of data array.

    RETURNS
    generator of tuple containing x-y array index values.

    """
    def intceil(x):
        return int(np.ceil(x))

    for i in range(intceil(i0-r), intceil(i0+r)):
        ri = np.sqrt(r**2-(i-i0)**2)
        for j in range(intceil(j0-ri), intceil(j0+ri)):
            if (i >= 0 and i < xlen) and (j >= 0 and j < ylen):
                # yield arr[i][j]
                yield((i, j))


def _points_in_polygon(i0, j0, polygon):
    # For each point in arr, check if it is inside polygon.
    # Not implemented, or required. Use _coreg_polygon
    pass


def _coreg_buffer(i0, j0, data, region):
    """
    Coregisters a point with a buffer region of a raster.

    INPUTS
    i0: column-index of point of interest
    j0: row-index of point of interest
    data: two-dimensional numpy array (raster)
    region: integer radius, same units as data resolution.

    RETURNS
    pts: all values from array within region
    """

    if (type(region) == float) or (type(region) == int):
        pts_iterator = _points_in_circle(
                            i0, j0, region, len(data[:, 0]), len(data[0, :]))

    else:
        print("This method only uses circular buffers defined by a radius. \
            For buffers defined by a polygon use _coreg_polygon. \
            ")

    # Convert list of returned tuples to indexes readable by the data array.
    pts = tuple(zip(*list(pts_iterator)))

    return data[pts]


def raster_buffer(long, lat, raster, buffer):
    """
    given a longitude,latitude point, a raster file, and a buffer region,
        return the value values of all points in circular buffer.

    INPUTS:
    long: longitude point of interest
    lat: latitude point of interest
    raster: file path/name (as string)
    buffer: integer, raster array pixel units to return values for

    RETURNS
    values: list of raster array values around point of interest.
    """
    print("Opening:", raster)
    # raster = gdal.Open(raster)
    raster = rasterio.open(raster)

    # Get the transformation crs data
    # gt = raster.GetGeoTransform()
    gt  = raster.transform

    # Interogate the tiff file as an array
    # This will only be the first band, usally multiband has same index.
    # arr = raster.GetRasterBand(1).ReadAsArray()
    arr = raster.read(1)

    # FIXME Check the number of bands and print a warning if more than 1

    # Shape of raster
    print("Raster pixel size:", np.shape(arr))

    # get row/column index of point
    point = utils._get_coords_at_point(gt, long, lat)

    # get values of data array at the buffer-index locations
    values = _coreg_buffer(point[0], point[1], arr, buffer)

    return(values)


def _get_features(gdf):
    """
    Function to parse features from GeoDataFrame in such a manner that
        rasterio wants them

    gdf: geodataframe of a geometry polygon.
    """
    return [json.loads(gdf.to_json())['features'][0]['geometry']]


def _coreg_polygon(data, polygon):
    """
    Crops a raster to a polygon shape.

    INPUTS
    data: gdal raster object.
    polygon: Shapely Polygon defining area to crop.

    RETURNS
    out_img: Returns array of values inside the polygon.
    """
    geo = gpd.GeoDataFrame({'geometry': polygon}, index=[0], crs=from_epsg(4326))
    geo = geo.to_crs(crs=data.crs.data)
    coords = _get_features(geo)
    out_img, _ = mask(data, shapes=coords, crop=True)

    return(out_img)


def raster_polygon_buffer(lngs, lats, raster):
    """
    Given a list of longitudes and latitudes that define a polygone, crop a
        raster file, and return the values of all points in the polygon.

    INPUTS:
    lngs: list of longitudes
    lats: list of latitudes
    raster: file path/name (as string) of raster

    RETURNS
    values: list of raster array values inside polygon.
    """
    if (len(lngs) != len(lats)):
        raise ValueError("Longitude and Latitude list should be equal in length\
            representing pairs of points defining a polygon.")

    print("Opening:", raster)
    # raster = gdal.Open(raster)
    raster = rasterio.open(raster)

    # get row/column index of point
    polygon = Polygon(list(zip(lngs, lats)))

    # get values of data array at the buffer-index locations
    values = _coreg_polygon(raster, polygon)

    return(values)

Functions

def raster_buffer(long, lat, raster, buffer)

given a longitude,latitude point, a raster file, and a buffer region, return the value values of all points in circular buffer.

INPUTS: long: longitude point of interest lat: latitude point of interest raster: file path/name (as string) buffer: integer, raster array pixel units to return values for

RETURNS values: list of raster array values around point of interest.

Expand source code
def raster_buffer(long, lat, raster, buffer):
    """
    given a longitude,latitude point, a raster file, and a buffer region,
        return the value values of all points in circular buffer.

    INPUTS:
    long: longitude point of interest
    lat: latitude point of interest
    raster: file path/name (as string)
    buffer: integer, raster array pixel units to return values for

    RETURNS
    values: list of raster array values around point of interest.
    """
    print("Opening:", raster)
    # raster = gdal.Open(raster)
    raster = rasterio.open(raster)

    # Get the transformation crs data
    # gt = raster.GetGeoTransform()
    gt  = raster.transform

    # Interogate the tiff file as an array
    # This will only be the first band, usally multiband has same index.
    # arr = raster.GetRasterBand(1).ReadAsArray()
    arr = raster.read(1)

    # FIXME Check the number of bands and print a warning if more than 1

    # Shape of raster
    print("Raster pixel size:", np.shape(arr))

    # get row/column index of point
    point = utils._get_coords_at_point(gt, long, lat)

    # get values of data array at the buffer-index locations
    values = _coreg_buffer(point[0], point[1], arr, buffer)

    return(values)
def raster_polygon_buffer(lngs, lats, raster)

Given a list of longitudes and latitudes that define a polygone, crop a raster file, and return the values of all points in the polygon.

INPUTS: lngs: list of longitudes lats: list of latitudes raster: file path/name (as string) of raster

RETURNS values: list of raster array values inside polygon.

Expand source code
def raster_polygon_buffer(lngs, lats, raster):
    """
    Given a list of longitudes and latitudes that define a polygone, crop a
        raster file, and return the values of all points in the polygon.

    INPUTS:
    lngs: list of longitudes
    lats: list of latitudes
    raster: file path/name (as string) of raster

    RETURNS
    values: list of raster array values inside polygon.
    """
    if (len(lngs) != len(lats)):
        raise ValueError("Longitude and Latitude list should be equal in length\
            representing pairs of points defining a polygon.")

    print("Opening:", raster)
    # raster = gdal.Open(raster)
    raster = rasterio.open(raster)

    # get row/column index of point
    polygon = Polygon(list(zip(lngs, lats)))

    # get values of data array at the buffer-index locations
    values = _coreg_polygon(raster, polygon)

    return(values)