Source code for sliderule.raster

# Copyright (c) 2021, University of Washington
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
#
# 2. 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.
#
# 3. Neither the name of the University of Washington 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 UNIVERSITY OF WASHINGTON 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 THE UNIVERSITY OF WASHINGTON OR
# CONTRIBUTORS 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.

import numpy
import base64
import sliderule
import geopandas
from shapely.geometry import Polygon

###############################################################################
# GLOBALS
###############################################################################

profiles = {}

###############################################################################
# UTILITIES
###############################################################################

#
# poly2bbox
#
def poly2bbox(poly):
    lats = [p["lat"] for p in poly]
    lons = [p["lon"] for p in poly]
    bbox = [ min(lons), min(lats), max(lons), max(lats) ]
    return bbox

###############################################################################
# APIs
###############################################################################

#
# Sample
#
[docs] def sample(asset, coordinates, parms={}): ''' Sample a raster dataset at the coordinates provided Parameters ---------- asset: str data source asset (see `Assets </web/rtd/user_guide/ICESat-2.html#assets>`_) coordinates: list list of coordinates as [longitude, latitude] parms: dict dictionary of sampling parameters Returns ------- GeoDataFrame geolocated sampled values along with metadata Examples -------- >>> import sliderule >>> gdf = sliderule.sample("arctidcem-mosaic", [[-178.0,51.7]]) ''' # Massage Arguments if type(coordinates[0]) != list: coordinates = [coordinates] # Perform Request rqst = {"samples": {"asset": asset, **parms}, "coordinates": coordinates} rsps = sliderule.source("samples", rqst) # Sanity Check Response if len(rsps) <= 0: return sliderule.emptyframe() # Count Records num_records = 0 for input_coord_response in rsps["samples"]: for raster_sample in input_coord_response: num_records += 1 # Build Initial Columns value_nptype = sliderule.get_definition("rsrec.sample", "value")['nptype'] columns = { 'time': numpy.empty(num_records, numpy.int64), 'latitude': numpy.empty(num_records, numpy.double), 'longitude': numpy.empty(num_records, numpy.double), 'file': [], 'value': numpy.empty(num_records, value_nptype) } if 'with_flags' in parms: flags_nptype = sliderule.get_definition("rsrec.sample", "flags")['nptype'] columns = { 'flags': numpy.empty(num_records, flags_nptype), **columns } if 'zonal_stats' in parms: count_nptype = sliderule.get_definition("zsrec.sample", "count")['nptype'] min_nptype = sliderule.get_definition("zsrec.sample", "min")['nptype'] max_nptype = sliderule.get_definition("zsrec.sample", "max")['nptype'] mean_nptype = sliderule.get_definition("zsrec.sample", "mean")['nptype'] median_nptype = sliderule.get_definition("zsrec.sample", "median")['nptype'] stdev_nptype = sliderule.get_definition("zsrec.sample", "stdev")['nptype'] mad_nptype = sliderule.get_definition("zsrec.sample", "mad")['nptype'] columns = { 'count': numpy.empty(num_records, count_nptype), 'min': numpy.empty(num_records, min_nptype), 'max': numpy.empty(num_records, max_nptype), 'mean': numpy.empty(num_records, mean_nptype), 'median': numpy.empty(num_records, median_nptype), 'stdev': numpy.empty(num_records, stdev_nptype), 'mad': numpy.empty(num_records, mad_nptype), **columns } # Populate Columns i = 0 per_coord_index = 0 for input_coord_response in rsps["samples"]: longitude = coordinates[per_coord_index][0] latitude = coordinates[per_coord_index][1] per_coord_index += 1 for raster_sample in input_coord_response: columns['time'][i] = numpy.int64((raster_sample['time'] + 315964800.0) * 1000000000) columns['longitude'][i] = numpy.double(longitude) columns['latitude'][i] = numpy.double(latitude) columns['file'] += raster_sample['file'], columns['value'][i] = value_nptype(raster_sample['value']) if 'with_flags' in parms: columns[i] = flags_nptype(raster_sample['flags']) if 'zonal_stats' in parms: columns[i] = count_nptype(raster_sample['count']) columns[i] = min_nptype(raster_sample['min']) columns[i] = max_nptype(raster_sample['max']) columns[i] = mean_nptype(raster_sample['mean']) columns[i] = median_nptype(raster_sample['median']) columns[i] = stdev_nptype(raster_sample['stdev']) columns[i] = mad_nptype(raster_sample['mad']) i += 1 # Build GeoDataFrame gdf = sliderule.todataframe(columns) return gdf
# # Subset #
[docs] def subset(asset, extents, parms={}): ''' Subset a raster dataset at the extent coordinates provided Parameters ---------- asset: str data source asset (see `Assets </web/rtd/user_guide/ICESat-2.html#assets>`_) extents: list list of extent coordinates as [minimum longitude, minimum latitude, maximum longitude, maximum latitude] parms: dict dictionary of sampling parameters Returns ------- list list of 2D numpy arrays containing the values of the subsetted raster Examples -------- >>> import sliderule >>> gdf = sliderule.subset("landsat-hls", [[-179.87, 50.45, -178.77, 50.75]], {"bands": ["B02"]}) ''' # Massage Arguments if type(extents[0]) != list: extents = [extents] # Perform Request rqst = {"samples": {"asset": asset, **parms}, "extents": extents} rsps = sliderule.source("subsets", rqst) # Count Size of Response subset_cnt = 0 for subsets in rsps['subsets']: subset_cnt += len(subsets) # Initialize Geometry and Columns geometry = numpy.empty(subset_cnt, dtype=object) columns = { "data": numpy.empty(subset_cnt, dtype=object), "rows": numpy.empty(subset_cnt, dtype=numpy.uint64), "cols": numpy.empty(subset_cnt, dtype=numpy.uint64), "time": numpy.empty(subset_cnt, dtype='datetime64[ns]'), "file": [] } # Build Geometry and Columns row_index = 0 extent_index = 0 for subsets in rsps['subsets']: extent = extents[extent_index] polygon = Polygon([(extent[0], extent[1]), (extent[0], extent[3]), (extent[2], extent[3]), (extent[2], extent[1])]) for subset in subsets: # Add Geometry geometry[row_index] = polygon # Add Row Elements columns["rows"][row_index] = numpy.uint64(subset["rows"]) columns["cols"][row_index] = numpy.uint64(subset["cols"]) columns["time"][row_index] = numpy.datetime64(subset["time"], 'ns') columns["file"].append(subset["file"]) data = sliderule.getvalues(base64.b64decode(subset["data"]), subset["datatype"], subset["size"]) data.shape = (subset["rows"], subset["cols"]) columns["data"][row_index] = data # Goto Next Row row_index += 1 # Goto Next Extent extent_index += 1 # Build and Return GeoDataFrame df = geopandas.pd.DataFrame(columns) gdf = geopandas.GeoDataFrame(df, geometry=geometry, crs=sliderule.SLIDERULE_EPSG) gdf.set_index("time", inplace=True) return gdf