mirror of
https://git.osgeo.org/gitea/postgis/postgis
synced 2024-10-23 16:42:35 +00:00
f6ed58d1fd
git-svn-id: http://svn.osgeo.org/postgis/trunk@14869 b70326c6-7e19-0410-871a-916f4a2858ee
1794 lines
43 KiB
C
1794 lines
43 KiB
C
/*
|
|
*
|
|
* WKTRaster - Raster Types for PostGIS
|
|
* http://trac.osgeo.org/postgis/wiki/WKTRaster
|
|
*
|
|
* Copyright (C) 2011-2013 Regents of the University of California
|
|
* <bkpark@ucdavis.edu>
|
|
* Copyright (C) 2010-2011 Jorge Arevalo <jorge.arevalo@deimos-space.com>
|
|
* Copyright (C) 2010-2011 David Zwarg <dzwarg@azavea.com>
|
|
* Copyright (C) 2009-2011 Pierre Racine <pierre.racine@sbf.ulaval.ca>
|
|
* Copyright (C) 2009-2011 Mateusz Loskot <mateusz@loskot.net>
|
|
* Copyright (C) 2008-2009 Sandro Santilli <strk@keybit.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "librtcore.h"
|
|
#include "librtcore_internal.h"
|
|
|
|
#include "gdal_vrt.h"
|
|
|
|
/**
|
|
* Create an in-db rt_band with no data
|
|
*
|
|
* @param width : number of pixel columns
|
|
* @param height : number of pixel rows
|
|
* @param pixtype : pixel type for the band
|
|
* @param hasnodata : indicates if the band has nodata value
|
|
* @param nodataval : the nodata value, will be appropriately
|
|
* truncated to fit the pixtype size.
|
|
* @param data : pointer to actual band data, required to
|
|
* be aligned accordingly to
|
|
* rt_pixtype_aligment(pixtype) and big enough
|
|
* to hold raster width*height values.
|
|
* Data will NOT be copied, ownership is left
|
|
* to caller which is responsible to keep it
|
|
* allocated for the whole lifetime of the returned
|
|
* rt_band.
|
|
*
|
|
* @return an rt_band, or 0 on failure
|
|
*/
|
|
rt_band
|
|
rt_band_new_inline(
|
|
uint16_t width, uint16_t height,
|
|
rt_pixtype pixtype,
|
|
uint32_t hasnodata, double nodataval,
|
|
uint8_t* data
|
|
) {
|
|
rt_band band = NULL;
|
|
|
|
assert(NULL != data);
|
|
|
|
band = rtalloc(sizeof(struct rt_band_t));
|
|
if (band == NULL) {
|
|
rterror("rt_band_new_inline: Out of memory allocating rt_band");
|
|
return NULL;
|
|
}
|
|
|
|
RASTER_DEBUGF(3, "Created rt_band @ %p with pixtype %s", band, rt_pixtype_name(pixtype));
|
|
|
|
band->pixtype = pixtype;
|
|
band->offline = 0;
|
|
band->width = width;
|
|
band->height = height;
|
|
band->hasnodata = hasnodata ? 1 : 0;
|
|
band->isnodata = FALSE; /* we don't know what is in data, so must be FALSE */
|
|
band->nodataval = 0;
|
|
band->data.mem = data;
|
|
band->ownsdata = 0; /* we do NOT own this data!!! */
|
|
band->raster = NULL;
|
|
|
|
RASTER_DEBUGF(3, "Created rt_band with dimensions %d x %d", band->width, band->height);
|
|
|
|
/* properly set nodataval as it may need to be constrained to the data type */
|
|
if (hasnodata && rt_band_set_nodata(band, nodataval, NULL) != ES_NONE) {
|
|
rterror("rt_band_new_inline: Could not set NODATA value");
|
|
rt_band_destroy(band);
|
|
return NULL;
|
|
}
|
|
|
|
return band;
|
|
}
|
|
|
|
/**
|
|
* Create an out-db rt_band
|
|
*
|
|
* @param width : number of pixel columns
|
|
* @param height : number of pixel rows
|
|
* @param pixtype : pixel type for the band
|
|
* @param hasnodata : indicates if the band has nodata value
|
|
* @param nodataval : the nodata value, will be appropriately
|
|
* truncated to fit the pixtype size.
|
|
* @param bandNum : 0-based band number in the external file
|
|
* to associate this band with.
|
|
* @param path : NULL-terminated path string pointing to the file
|
|
* containing band data. The string will NOT be
|
|
* copied, ownership is left to caller which is
|
|
* responsible to keep it allocated for the whole
|
|
* lifetime of the returned rt_band.
|
|
*
|
|
* @return an rt_band, or 0 on failure
|
|
*/
|
|
rt_band
|
|
rt_band_new_offline(
|
|
uint16_t width, uint16_t height,
|
|
rt_pixtype pixtype,
|
|
uint32_t hasnodata, double nodataval,
|
|
uint8_t bandNum, const char* path
|
|
) {
|
|
rt_band band = NULL;
|
|
int pathlen = 0;
|
|
|
|
assert(NULL != path);
|
|
|
|
band = rtalloc(sizeof(struct rt_band_t));
|
|
if (band == NULL) {
|
|
rterror("rt_band_new_offline: Out of memory allocating rt_band");
|
|
return NULL;
|
|
}
|
|
|
|
RASTER_DEBUGF(3, "Created rt_band @ %p with pixtype %s",
|
|
band, rt_pixtype_name(pixtype)
|
|
);
|
|
|
|
band->pixtype = pixtype;
|
|
band->offline = 1;
|
|
band->width = width;
|
|
band->height = height;
|
|
band->hasnodata = hasnodata ? 1 : 0;
|
|
band->nodataval = 0;
|
|
band->isnodata = FALSE; /* we don't know if the offline band is NODATA */
|
|
band->ownsdata = 0; /* offline, flag is useless as all offline data cache is owned internally */
|
|
band->raster = NULL;
|
|
|
|
/* properly set nodataval as it may need to be constrained to the data type */
|
|
if (hasnodata && rt_band_set_nodata(band, nodataval, NULL) != ES_NONE) {
|
|
rterror("rt_band_new_offline: Could not set NODATA value");
|
|
rt_band_destroy(band);
|
|
return NULL;
|
|
}
|
|
|
|
band->data.offline.bandNum = bandNum;
|
|
|
|
/* memory for data.offline.path is managed internally */
|
|
pathlen = strlen(path);
|
|
band->data.offline.path = rtalloc(sizeof(char) * (pathlen + 1));
|
|
if (band->data.offline.path == NULL) {
|
|
rterror("rt_band_new_offline: Out of memory allocating offline path");
|
|
rt_band_destroy(band);
|
|
return NULL;
|
|
}
|
|
memcpy(band->data.offline.path, path, pathlen);
|
|
band->data.offline.path[pathlen] = '\0';
|
|
|
|
band->data.offline.mem = NULL;
|
|
|
|
return band;
|
|
}
|
|
|
|
/**
|
|
* Create a new band duplicated from source band. Memory is allocated
|
|
* for band path (if band is offline) or band data (if band is online).
|
|
* The caller is responsible for freeing the memory when the returned
|
|
* rt_band is destroyed.
|
|
*
|
|
* @param : the band to copy
|
|
*
|
|
* @return an rt_band or NULL on failure
|
|
*/
|
|
rt_band
|
|
rt_band_duplicate(rt_band band) {
|
|
rt_band rtn = NULL;
|
|
|
|
assert(band != NULL);
|
|
|
|
/* offline */
|
|
if (band->offline) {
|
|
rtn = rt_band_new_offline(
|
|
band->width, band->height,
|
|
band->pixtype,
|
|
band->hasnodata, band->nodataval,
|
|
band->data.offline.bandNum, (const char *) band->data.offline.path
|
|
);
|
|
}
|
|
/* online */
|
|
else {
|
|
uint8_t *data = NULL;
|
|
data = rtalloc(rt_pixtype_size(band->pixtype) * band->width * band->height);
|
|
if (data == NULL) {
|
|
rterror("rt_band_duplicate: Out of memory allocating online band data");
|
|
return NULL;
|
|
}
|
|
memcpy(data, band->data.mem, rt_pixtype_size(band->pixtype) * band->width * band->height);
|
|
|
|
rtn = rt_band_new_inline(
|
|
band->width, band->height,
|
|
band->pixtype,
|
|
band->hasnodata, band->nodataval,
|
|
data
|
|
);
|
|
rt_band_set_ownsdata_flag(rtn, 1); /* we DO own this data!!! */
|
|
}
|
|
|
|
if (rtn == NULL) {
|
|
rterror("rt_band_duplicate: Could not copy band");
|
|
return NULL;
|
|
}
|
|
|
|
return rtn;
|
|
}
|
|
|
|
int
|
|
rt_band_is_offline(rt_band band) {
|
|
|
|
assert(NULL != band);
|
|
|
|
|
|
return band->offline ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Destroy a raster band
|
|
*
|
|
* @param band : the band to destroy
|
|
*/
|
|
void
|
|
rt_band_destroy(rt_band band) {
|
|
if (band == NULL)
|
|
return;
|
|
|
|
RASTER_DEBUGF(3, "Destroying rt_band @ %p", band);
|
|
|
|
/* offline band */
|
|
if (band->offline) {
|
|
/* memory cache */
|
|
if (band->data.offline.mem != NULL)
|
|
rtdealloc(band->data.offline.mem);
|
|
/* offline file path */
|
|
if (band->data.offline.path != NULL)
|
|
rtdealloc(band->data.offline.path);
|
|
}
|
|
/* inline band and band owns the data */
|
|
else if (band->data.mem != NULL && band->ownsdata)
|
|
rtdealloc(band->data.mem);
|
|
|
|
rtdealloc(band);
|
|
}
|
|
|
|
const char*
|
|
rt_band_get_ext_path(rt_band band) {
|
|
|
|
assert(NULL != band);
|
|
|
|
|
|
if (!band->offline) {
|
|
RASTER_DEBUG(3, "rt_band_get_ext_path: Band is not offline");
|
|
return NULL;
|
|
}
|
|
return band->data.offline.path;
|
|
}
|
|
|
|
rt_errorstate
|
|
rt_band_get_ext_band_num(rt_band band, uint8_t *bandnum) {
|
|
assert(NULL != band);
|
|
assert(NULL != bandnum);
|
|
|
|
*bandnum = 0;
|
|
|
|
if (!band->offline) {
|
|
RASTER_DEBUG(3, "rt_band_get_ext_band_num: Band is not offline");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
*bandnum = band->data.offline.bandNum;
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/**
|
|
* Get pointer to raster band data
|
|
*
|
|
* @param band : the band who's data to get
|
|
*
|
|
* @return pointer to band data or NULL if error
|
|
*/
|
|
void *
|
|
rt_band_get_data(rt_band band) {
|
|
assert(NULL != band);
|
|
|
|
if (band->offline) {
|
|
if (band->data.offline.mem != NULL)
|
|
return band->data.offline.mem;
|
|
|
|
if (rt_band_load_offline_data(band) != ES_NONE)
|
|
return NULL;
|
|
else
|
|
return band->data.offline.mem;
|
|
}
|
|
else
|
|
return band->data.mem;
|
|
}
|
|
|
|
/* variable for PostgreSQL GUC: postgis.enable_outdb_rasters */
|
|
char enable_outdb_rasters = 1;
|
|
|
|
/**
|
|
* Load offline band's data. Loaded data is internally owned
|
|
* and should not be released by the caller. Data will be
|
|
* released when band is destroyed with rt_band_destroy().
|
|
*
|
|
* @param band : the band who's data to get
|
|
*
|
|
* @return ES_NONE if success, ES_ERROR if failure
|
|
*/
|
|
rt_errorstate
|
|
rt_band_load_offline_data(rt_band band) {
|
|
GDALDatasetH hdsSrc = NULL;
|
|
int nband = 0;
|
|
VRTDatasetH hdsDst = NULL;
|
|
VRTSourcedRasterBandH hbandDst = NULL;
|
|
double gt[6] = {0.};
|
|
double ogt[6] = {0};
|
|
double offset[2] = {0};
|
|
|
|
rt_raster _rast = NULL;
|
|
rt_band _band = NULL;
|
|
int aligned = 0;
|
|
int err = ES_NONE;
|
|
|
|
assert(band != NULL);
|
|
assert(band->raster != NULL);
|
|
|
|
if (!band->offline) {
|
|
rterror("rt_band_load_offline_data: Band is not offline");
|
|
return ES_ERROR;
|
|
}
|
|
else if (!strlen(band->data.offline.path)) {
|
|
rterror("rt_band_load_offline_data: Offline band does not a have a specified file");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* offline_data is disabled */
|
|
if (!enable_outdb_rasters) {
|
|
rterror("rt_band_load_offline_data: Access to offline bands disabled");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
rt_util_gdal_register_all(0);
|
|
/*
|
|
hdsSrc = rt_util_gdal_open(band->data.offline.path, GA_ReadOnly, 1);
|
|
*/
|
|
hdsSrc = rt_util_gdal_open(band->data.offline.path, GA_ReadOnly, 0);
|
|
if (hdsSrc == NULL) {
|
|
rterror("rt_band_load_offline_data: Cannot open offline raster: %s", band->data.offline.path);
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* # of bands */
|
|
nband = GDALGetRasterCount(hdsSrc);
|
|
if (!nband) {
|
|
rterror("rt_band_load_offline_data: No bands found in offline raster: %s", band->data.offline.path);
|
|
GDALClose(hdsSrc);
|
|
return ES_ERROR;
|
|
}
|
|
/* bandNum is 0-based */
|
|
else if (band->data.offline.bandNum + 1 > nband) {
|
|
rterror("rt_band_load_offline_data: Specified band %d not found in offline raster: %s", band->data.offline.bandNum, band->data.offline.path);
|
|
GDALClose(hdsSrc);
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* get raster's geotransform */
|
|
rt_raster_get_geotransform_matrix(band->raster, gt);
|
|
RASTER_DEBUGF(3, "Raster geotransform (%f, %f, %f, %f, %f, %f)",
|
|
gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
|
|
|
|
/* get offline raster's geotransform */
|
|
if (GDALGetGeoTransform(hdsSrc, ogt) != CE_None) {
|
|
RASTER_DEBUG(4, "Using default geotransform matrix (0, 1, 0, 0, 0, -1)");
|
|
ogt[0] = 0;
|
|
ogt[1] = 1;
|
|
ogt[2] = 0;
|
|
ogt[3] = 0;
|
|
ogt[4] = 0;
|
|
ogt[5] = -1;
|
|
}
|
|
RASTER_DEBUGF(3, "Offline geotransform (%f, %f, %f, %f, %f, %f)",
|
|
ogt[0], ogt[1], ogt[2], ogt[3], ogt[4], ogt[5]);
|
|
|
|
/* are rasters aligned? */
|
|
_rast = rt_raster_new(1, 1);
|
|
rt_raster_set_geotransform_matrix(_rast, ogt);
|
|
rt_raster_set_srid(_rast, band->raster->srid);
|
|
err = rt_raster_same_alignment(band->raster, _rast, &aligned, NULL);
|
|
rt_raster_destroy(_rast);
|
|
|
|
if (err != ES_NONE) {
|
|
rterror("rt_band_load_offline_data: Could not test alignment of in-db representation of out-db raster");
|
|
GDALClose(hdsSrc);
|
|
return ES_ERROR;
|
|
}
|
|
else if (!aligned) {
|
|
rtwarn("The in-db representation of the out-db raster is not aligned. Band data may be incorrect");
|
|
}
|
|
|
|
/* get offsets */
|
|
rt_raster_geopoint_to_cell(
|
|
band->raster,
|
|
ogt[0], ogt[3],
|
|
&(offset[0]), &(offset[1]),
|
|
NULL
|
|
);
|
|
|
|
RASTER_DEBUGF(4, "offsets: (%f, %f)", offset[0], offset[1]);
|
|
|
|
/* create VRT dataset */
|
|
hdsDst = VRTCreate(band->width, band->height);
|
|
GDALSetGeoTransform(hdsDst, gt);
|
|
/*
|
|
GDALSetDescription(hdsDst, "/tmp/offline.vrt");
|
|
*/
|
|
|
|
/* add band as simple sources */
|
|
GDALAddBand(hdsDst, rt_util_pixtype_to_gdal_datatype(band->pixtype), NULL);
|
|
hbandDst = (VRTSourcedRasterBandH) GDALGetRasterBand(hdsDst, 1);
|
|
|
|
if (band->hasnodata)
|
|
GDALSetRasterNoDataValue(hbandDst, band->nodataval);
|
|
|
|
VRTAddSimpleSource(
|
|
hbandDst, GDALGetRasterBand(hdsSrc, band->data.offline.bandNum + 1),
|
|
fabs(offset[0]), fabs(offset[1]),
|
|
band->width, band->height,
|
|
0, 0,
|
|
band->width, band->height,
|
|
"near", VRT_NODATA_UNSET
|
|
);
|
|
|
|
/* make sure VRT reflects all changes */
|
|
VRTFlushCache(hdsDst);
|
|
|
|
/* convert VRT dataset to rt_raster */
|
|
_rast = rt_raster_from_gdal_dataset(hdsDst);
|
|
|
|
GDALClose(hdsDst);
|
|
GDALClose(hdsSrc);
|
|
/*
|
|
{
|
|
FILE *fp;
|
|
fp = fopen("/tmp/gdal_open_files.log", "w");
|
|
GDALDumpOpenDatasets(fp);
|
|
fclose(fp);
|
|
}
|
|
*/
|
|
|
|
if (_rast == NULL) {
|
|
rterror("rt_band_load_offline_data: Cannot load data from offline raster: %s", band->data.offline.path);
|
|
return ES_ERROR;
|
|
}
|
|
|
|
_band = rt_raster_get_band(_rast, 0);
|
|
if (_band == NULL) {
|
|
rterror("rt_band_load_offline_data: Cannot load data from offline raster: %s", band->data.offline.path);
|
|
rt_raster_destroy(_rast);
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* band->data.offline.mem not NULL, free first */
|
|
if (band->data.offline.mem != NULL) {
|
|
rtdealloc(band->data.offline.mem);
|
|
band->data.offline.mem = NULL;
|
|
}
|
|
|
|
band->data.offline.mem = _band->data.mem;
|
|
|
|
rtdealloc(_band); /* cannot use rt_band_destroy */
|
|
rt_raster_destroy(_rast);
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
rt_pixtype
|
|
rt_band_get_pixtype(rt_band band) {
|
|
|
|
assert(NULL != band);
|
|
|
|
|
|
return band->pixtype;
|
|
}
|
|
|
|
uint16_t
|
|
rt_band_get_width(rt_band band) {
|
|
|
|
assert(NULL != band);
|
|
|
|
|
|
return band->width;
|
|
}
|
|
|
|
uint16_t
|
|
rt_band_get_height(rt_band band) {
|
|
|
|
assert(NULL != band);
|
|
|
|
|
|
return band->height;
|
|
}
|
|
|
|
/* Get ownsdata flag */
|
|
int
|
|
rt_band_get_ownsdata_flag(rt_band band) {
|
|
assert(NULL != band);
|
|
|
|
return band->ownsdata ? 1 : 0;
|
|
}
|
|
|
|
/* set ownsdata flag */
|
|
void
|
|
rt_band_set_ownsdata_flag(rt_band band, int flag) {
|
|
assert(NULL != band);
|
|
|
|
band->ownsdata = flag ? 1 : 0;
|
|
}
|
|
|
|
int
|
|
rt_band_get_hasnodata_flag(rt_band band) {
|
|
assert(NULL != band);
|
|
|
|
return band->hasnodata ? 1 : 0;
|
|
}
|
|
|
|
void
|
|
rt_band_set_hasnodata_flag(rt_band band, int flag) {
|
|
|
|
assert(NULL != band);
|
|
|
|
band->hasnodata = (flag) ? 1 : 0;
|
|
|
|
/* isnodata depends on hasnodata */
|
|
if (!band->hasnodata && band->isnodata) {
|
|
RASTER_DEBUG(3, "Setting isnodata to FALSE as band no longer has NODATA");
|
|
band->isnodata = 0;
|
|
}
|
|
}
|
|
|
|
rt_errorstate
|
|
rt_band_set_isnodata_flag(rt_band band, int flag) {
|
|
assert(NULL != band);
|
|
|
|
if (!band->hasnodata) {
|
|
/* silently permit setting isnodata flag to FALSE */
|
|
if (!flag)
|
|
band->isnodata = 0;
|
|
else {
|
|
rterror("rt_band_set_isnodata_flag: Cannot set isnodata flag as band has no NODATA");
|
|
return ES_ERROR;
|
|
}
|
|
}
|
|
else
|
|
band->isnodata = (flag) ? 1 : 0;
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
int
|
|
rt_band_get_isnodata_flag(rt_band band) {
|
|
assert(NULL != band);
|
|
|
|
if (band->hasnodata)
|
|
return band->isnodata ? 1 : 0;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set nodata value
|
|
*
|
|
* @param band : the band to set nodata value to
|
|
* @param val : the nodata value
|
|
* @param converted : if non-zero, value was truncated/clamped/coverted
|
|
*
|
|
* @return ES_NONE or ES_ERROR
|
|
*/
|
|
rt_errorstate
|
|
rt_band_set_nodata(rt_band band, double val, int *converted) {
|
|
rt_pixtype pixtype = PT_END;
|
|
int32_t checkvalint = 0;
|
|
uint32_t checkvaluint = 0;
|
|
float checkvalfloat = 0;
|
|
double checkvaldouble = 0;
|
|
|
|
assert(NULL != band);
|
|
|
|
if (converted != NULL)
|
|
*converted = 0;
|
|
|
|
pixtype = band->pixtype;
|
|
|
|
RASTER_DEBUGF(3, "rt_band_set_nodata: setting nodata value %g with band type %s", val, rt_pixtype_name(pixtype));
|
|
|
|
/* return -1 on out of range */
|
|
switch (pixtype) {
|
|
case PT_1BB: {
|
|
band->nodataval = rt_util_clamp_to_1BB(val);
|
|
checkvalint = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_2BUI: {
|
|
band->nodataval = rt_util_clamp_to_2BUI(val);
|
|
checkvalint = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_4BUI: {
|
|
band->nodataval = rt_util_clamp_to_4BUI(val);
|
|
checkvalint = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_8BSI: {
|
|
band->nodataval = rt_util_clamp_to_8BSI(val);
|
|
checkvalint = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_8BUI: {
|
|
band->nodataval = rt_util_clamp_to_8BUI(val);
|
|
checkvalint = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_16BSI: {
|
|
band->nodataval = rt_util_clamp_to_16BSI(val);
|
|
checkvalint = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_16BUI: {
|
|
band->nodataval = rt_util_clamp_to_16BUI(val);
|
|
checkvalint = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_32BSI: {
|
|
band->nodataval = rt_util_clamp_to_32BSI(val);
|
|
checkvalint = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_32BUI: {
|
|
band->nodataval = rt_util_clamp_to_32BUI(val);
|
|
checkvaluint = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_32BF: {
|
|
band->nodataval = rt_util_clamp_to_32F(val);
|
|
checkvalfloat = band->nodataval;
|
|
break;
|
|
}
|
|
case PT_64BF: {
|
|
band->nodataval = val;
|
|
checkvaldouble = band->nodataval;
|
|
break;
|
|
}
|
|
default: {
|
|
rterror("rt_band_set_nodata: Unknown pixeltype %d", pixtype);
|
|
band->hasnodata = 0;
|
|
return ES_ERROR;
|
|
}
|
|
}
|
|
|
|
RASTER_DEBUGF(3, "rt_band_set_nodata: band->hasnodata = %d", band->hasnodata);
|
|
RASTER_DEBUGF(3, "rt_band_set_nodata: band->nodataval = %f", band->nodataval);
|
|
/* the nodata value was just set, so this band has NODATA */
|
|
band->hasnodata = 1;
|
|
|
|
/* also set isnodata flag to false */
|
|
band->isnodata = 0;
|
|
|
|
if (rt_util_dbl_trunc_warning(
|
|
val,
|
|
checkvalint, checkvaluint,
|
|
checkvalfloat, checkvaldouble,
|
|
pixtype
|
|
) && converted != NULL) {
|
|
*converted = 1;
|
|
}
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/**
|
|
* Set values of multiple pixels. Unlike rt_band_set_pixel,
|
|
* values in vals are expected to be of the band's pixel type
|
|
* as this function uses memcpy.
|
|
*
|
|
* It is important to be careful when using this function as
|
|
* the number of values being set may exceed a pixel "row".
|
|
* Remember that the band values are stored in a stream (1-D array)
|
|
* regardless of what the raster's width and height might be.
|
|
* So, setting a number of values may cross multiple pixel "rows".
|
|
*
|
|
* @param band : the band to set value to
|
|
* @param x : X coordinate (0-based)
|
|
* @param y : Y coordinate (0-based)
|
|
* @param vals : the pixel values to apply
|
|
* @param len : # of elements in vals
|
|
*
|
|
* @return ES_NONE on success, ES_ERROR on error
|
|
*/
|
|
rt_errorstate
|
|
rt_band_set_pixel_line(
|
|
rt_band band,
|
|
int x, int y,
|
|
void *vals, uint32_t len
|
|
) {
|
|
rt_pixtype pixtype = PT_END;
|
|
int size = 0;
|
|
uint8_t *data = NULL;
|
|
uint32_t offset = 0;
|
|
|
|
assert(NULL != band);
|
|
assert(vals != NULL && len > 0);
|
|
|
|
RASTER_DEBUGF(3, "length of values = %d", len);
|
|
|
|
if (band->offline) {
|
|
rterror("rt_band_set_pixel_line not implemented yet for OFFDB bands");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
pixtype = band->pixtype;
|
|
size = rt_pixtype_size(pixtype);
|
|
|
|
if (
|
|
x < 0 || x >= band->width ||
|
|
y < 0 || y >= band->height
|
|
) {
|
|
rterror("rt_band_set_pixel_line: Coordinates out of range (%d, %d) vs (%d, %d)", x, y, band->width, band->height);
|
|
return ES_ERROR;
|
|
}
|
|
|
|
data = rt_band_get_data(band);
|
|
offset = x + (y * band->width);
|
|
RASTER_DEBUGF(4, "offset = %d", offset);
|
|
|
|
/* make sure len of values to copy don't exceed end of data */
|
|
if (len > (band->width * band->height) - offset) {
|
|
rterror("rt_band_set_pixel_line: Could not apply pixels as values length exceeds end of data");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
switch (pixtype) {
|
|
case PT_1BB:
|
|
case PT_2BUI:
|
|
case PT_4BUI:
|
|
case PT_8BUI:
|
|
case PT_8BSI: {
|
|
uint8_t *ptr = data;
|
|
ptr += offset;
|
|
memcpy(ptr, vals, size * len);
|
|
break;
|
|
}
|
|
case PT_16BUI: {
|
|
uint16_t *ptr = (uint16_t *) data;
|
|
ptr += offset;
|
|
memcpy(ptr, vals, size * len);
|
|
break;
|
|
}
|
|
case PT_16BSI: {
|
|
int16_t *ptr = (int16_t *) data;
|
|
ptr += offset;
|
|
memcpy(ptr, vals, size * len);
|
|
break;
|
|
}
|
|
case PT_32BUI: {
|
|
uint32_t *ptr = (uint32_t *) data;
|
|
ptr += offset;
|
|
memcpy(ptr, vals, size * len);
|
|
break;
|
|
}
|
|
case PT_32BSI: {
|
|
int32_t *ptr = (int32_t *) data;
|
|
ptr += offset;
|
|
memcpy(ptr, vals, size * len);
|
|
break;
|
|
}
|
|
case PT_32BF: {
|
|
float *ptr = (float *) data;
|
|
ptr += offset;
|
|
memcpy(ptr, vals, size * len);
|
|
break;
|
|
}
|
|
case PT_64BF: {
|
|
double *ptr = (double *) data;
|
|
ptr += offset;
|
|
memcpy(ptr, vals, size * len);
|
|
break;
|
|
}
|
|
default: {
|
|
rterror("rt_band_set_pixel_line: Unknown pixeltype %d", pixtype);
|
|
return ES_ERROR;
|
|
}
|
|
}
|
|
|
|
#if POSTGIS_DEBUG_LEVEL > 0
|
|
{
|
|
double value;
|
|
rt_band_get_pixel(band, x, y, &value, NULL);
|
|
RASTER_DEBUGF(4, "pixel at (%d, %d) = %f", x, y, value);
|
|
}
|
|
#endif
|
|
|
|
/* set band's isnodata flag to FALSE */
|
|
if (rt_band_get_hasnodata_flag(band))
|
|
rt_band_set_isnodata_flag(band, 0);
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/**
|
|
* Set single pixel's value
|
|
*
|
|
* @param band : the band to set value to
|
|
* @param x : x ordinate (0-based)
|
|
* @param y : y ordinate (0-based)
|
|
* @param val : the pixel value
|
|
* @param converted : (optional) non-zero if value truncated/clamped/converted
|
|
*
|
|
* @return ES_NONE on success, ES_ERROR on error
|
|
*/
|
|
rt_errorstate
|
|
rt_band_set_pixel(
|
|
rt_band band,
|
|
int x, int y,
|
|
double val,
|
|
int *converted
|
|
) {
|
|
rt_pixtype pixtype = PT_END;
|
|
unsigned char* data = NULL;
|
|
uint32_t offset = 0;
|
|
|
|
int32_t checkvalint = 0;
|
|
uint32_t checkvaluint = 0;
|
|
float checkvalfloat = 0;
|
|
double checkvaldouble = 0;
|
|
|
|
assert(NULL != band);
|
|
|
|
if (converted != NULL)
|
|
*converted = 0;
|
|
|
|
if (band->offline) {
|
|
rterror("rt_band_set_pixel not implemented yet for OFFDB bands");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
pixtype = band->pixtype;
|
|
|
|
if (
|
|
x < 0 || x >= band->width ||
|
|
y < 0 || y >= band->height
|
|
) {
|
|
rterror("rt_band_set_pixel: Coordinates out of range");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* check that clamped value isn't clamped NODATA */
|
|
if (band->hasnodata && pixtype != PT_64BF) {
|
|
double newval;
|
|
int corrected;
|
|
|
|
rt_band_corrected_clamped_value(band, val, &newval, &corrected);
|
|
|
|
if (corrected) {
|
|
#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
|
|
rtwarn("Value for pixel %d x %d has been corrected as clamped value becomes NODATA", x, y);
|
|
#endif
|
|
val = newval;
|
|
|
|
if (converted != NULL)
|
|
*converted = 1;
|
|
}
|
|
}
|
|
|
|
data = rt_band_get_data(band);
|
|
offset = x + (y * band->width);
|
|
|
|
switch (pixtype) {
|
|
case PT_1BB: {
|
|
data[offset] = rt_util_clamp_to_1BB(val);
|
|
checkvalint = data[offset];
|
|
break;
|
|
}
|
|
case PT_2BUI: {
|
|
data[offset] = rt_util_clamp_to_2BUI(val);
|
|
checkvalint = data[offset];
|
|
break;
|
|
}
|
|
case PT_4BUI: {
|
|
data[offset] = rt_util_clamp_to_4BUI(val);
|
|
checkvalint = data[offset];
|
|
break;
|
|
}
|
|
case PT_8BSI: {
|
|
data[offset] = rt_util_clamp_to_8BSI(val);
|
|
checkvalint = (int8_t) data[offset];
|
|
break;
|
|
}
|
|
case PT_8BUI: {
|
|
data[offset] = rt_util_clamp_to_8BUI(val);
|
|
checkvalint = data[offset];
|
|
break;
|
|
}
|
|
case PT_16BSI: {
|
|
int16_t *ptr = (int16_t*) data; /* we assume correct alignment */
|
|
ptr[offset] = rt_util_clamp_to_16BSI(val);
|
|
checkvalint = (int16_t) ptr[offset];
|
|
break;
|
|
}
|
|
case PT_16BUI: {
|
|
uint16_t *ptr = (uint16_t*) data; /* we assume correct alignment */
|
|
ptr[offset] = rt_util_clamp_to_16BUI(val);
|
|
checkvalint = ptr[offset];
|
|
break;
|
|
}
|
|
case PT_32BSI: {
|
|
int32_t *ptr = (int32_t*) data; /* we assume correct alignment */
|
|
ptr[offset] = rt_util_clamp_to_32BSI(val);
|
|
checkvalint = (int32_t) ptr[offset];
|
|
break;
|
|
}
|
|
case PT_32BUI: {
|
|
uint32_t *ptr = (uint32_t*) data; /* we assume correct alignment */
|
|
ptr[offset] = rt_util_clamp_to_32BUI(val);
|
|
checkvaluint = ptr[offset];
|
|
break;
|
|
}
|
|
case PT_32BF: {
|
|
float *ptr = (float*) data; /* we assume correct alignment */
|
|
ptr[offset] = rt_util_clamp_to_32F(val);
|
|
checkvalfloat = ptr[offset];
|
|
break;
|
|
}
|
|
case PT_64BF: {
|
|
double *ptr = (double*) data; /* we assume correct alignment */
|
|
ptr[offset] = val;
|
|
checkvaldouble = ptr[offset];
|
|
break;
|
|
}
|
|
default: {
|
|
rterror("rt_band_set_pixel: Unknown pixeltype %d", pixtype);
|
|
return ES_ERROR;
|
|
}
|
|
}
|
|
|
|
/* If the stored value is not NODATA, reset the isnodata flag */
|
|
if (!rt_band_clamped_value_is_nodata(band, val)) {
|
|
RASTER_DEBUG(3, "Band has a value that is not NODATA. Setting isnodata to FALSE");
|
|
band->isnodata = FALSE;
|
|
}
|
|
|
|
/* Overflow checking */
|
|
if (rt_util_dbl_trunc_warning(
|
|
val,
|
|
checkvalint, checkvaluint,
|
|
checkvalfloat, checkvaldouble,
|
|
pixtype
|
|
) && converted != NULL) {
|
|
*converted = 1;
|
|
}
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/**
|
|
* Get values of multiple pixels. Unlike rt_band_get_pixel,
|
|
* values in vals are of the band's pixel type so cannot be
|
|
* assumed to be double. Function uses memcpy.
|
|
*
|
|
* It is important to be careful when using this function as
|
|
* the number of values being fetched may exceed a pixel "row".
|
|
* Remember that the band values are stored in a stream (1-D array)
|
|
* regardless of what the raster's width and height might be.
|
|
* So, getting a number of values may cross multiple pixel "rows".
|
|
*
|
|
* @param band : the band to get pixel value from
|
|
* @param x : pixel column (0-based)
|
|
* @param y : pixel row (0-based)
|
|
* @param len : the number of pixels to get
|
|
* @param **vals : the pixel values
|
|
* @param *nvals : the number of pixel values being returned
|
|
*
|
|
* @return ES_NONE on success, ES_ERROR on error
|
|
*/
|
|
rt_errorstate rt_band_get_pixel_line(
|
|
rt_band band,
|
|
int x, int y,
|
|
uint16_t len,
|
|
void **vals, uint16_t *nvals
|
|
) {
|
|
uint8_t *_vals = NULL;
|
|
int pixsize = 0;
|
|
uint8_t *data = NULL;
|
|
uint32_t offset = 0;
|
|
uint16_t _nvals = 0;
|
|
int maxlen = 0;
|
|
uint8_t *ptr = NULL;
|
|
|
|
assert(NULL != band);
|
|
assert(vals != NULL && nvals != NULL);
|
|
|
|
/* initialize to no values */
|
|
*nvals = 0;
|
|
|
|
if (
|
|
x < 0 || x >= band->width ||
|
|
y < 0 || y >= band->height
|
|
) {
|
|
rtwarn("Attempting to get pixel values with out of range raster coordinates: (%d, %d)", x, y);
|
|
return ES_ERROR;
|
|
}
|
|
|
|
if (len < 1)
|
|
return ES_NONE;
|
|
|
|
data = rt_band_get_data(band);
|
|
if (data == NULL) {
|
|
rterror("rt_band_get_pixel_line: Cannot get band data");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* +1 for the nodata value */
|
|
offset = x + (y * band->width);
|
|
RASTER_DEBUGF(4, "offset = %d", offset);
|
|
|
|
pixsize = rt_pixtype_size(band->pixtype);
|
|
RASTER_DEBUGF(4, "pixsize = %d", pixsize);
|
|
|
|
/* cap _nvals so that it doesn't overflow */
|
|
_nvals = len;
|
|
maxlen = band->width * band->height;
|
|
|
|
if (((int) (offset + _nvals)) > maxlen) {
|
|
_nvals = maxlen - offset;
|
|
rtwarn("Limiting returning number values to %d", _nvals);
|
|
}
|
|
RASTER_DEBUGF(4, "_nvals = %d", _nvals);
|
|
|
|
ptr = data + (offset * pixsize);
|
|
|
|
_vals = rtalloc(_nvals * pixsize);
|
|
if (_vals == NULL) {
|
|
rterror("rt_band_get_pixel_line: Could not allocate memory for pixel values");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* copy pixels */
|
|
memcpy(_vals, ptr, _nvals * pixsize);
|
|
|
|
*vals = _vals;
|
|
*nvals = _nvals;
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/**
|
|
* Get pixel value. If band's isnodata flag is TRUE, value returned
|
|
* will be the band's NODATA value
|
|
*
|
|
* @param band : the band to set nodata value to
|
|
* @param x : x ordinate (0-based)
|
|
* @param y : x ordinate (0-based)
|
|
* @param *value : pixel value
|
|
* @param *nodata : 0 if pixel is not NODATA
|
|
*
|
|
* @return 0 on success, -1 on error (value out of valid range).
|
|
*/
|
|
rt_errorstate
|
|
rt_band_get_pixel(
|
|
rt_band band,
|
|
int x, int y,
|
|
double *value,
|
|
int *nodata
|
|
) {
|
|
rt_pixtype pixtype = PT_END;
|
|
uint8_t* data = NULL;
|
|
uint32_t offset = 0;
|
|
|
|
assert(NULL != band);
|
|
assert(NULL != value);
|
|
|
|
/* set nodata to 0 */
|
|
if (nodata != NULL)
|
|
*nodata = 0;
|
|
|
|
if (
|
|
x < 0 || x >= band->width ||
|
|
y < 0 || y >= band->height
|
|
) {
|
|
rtwarn("Attempting to get pixel value with out of range raster coordinates: (%d, %d)", x, y);
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* band is NODATA */
|
|
if (band->isnodata) {
|
|
RASTER_DEBUG(3, "Band's isnodata flag is TRUE. Returning NODATA value");
|
|
*value = band->nodataval;
|
|
if (nodata != NULL) *nodata = 1;
|
|
return ES_NONE;
|
|
}
|
|
|
|
data = rt_band_get_data(band);
|
|
if (data == NULL) {
|
|
rterror("rt_band_get_pixel: Cannot get band data");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* +1 for the nodata value */
|
|
offset = x + (y * band->width);
|
|
|
|
pixtype = band->pixtype;
|
|
|
|
switch (pixtype) {
|
|
case PT_1BB:
|
|
#ifdef OPTIMIZE_SPACE
|
|
{
|
|
int byteOffset = offset / 8;
|
|
int bitOffset = offset % 8;
|
|
data += byteOffset;
|
|
|
|
/* Bit to set is bitOffset into data */
|
|
*value = getBits(data, val, 1, bitOffset);
|
|
break;
|
|
}
|
|
#endif
|
|
case PT_2BUI:
|
|
#ifdef OPTIMIZE_SPACE
|
|
{
|
|
int byteOffset = offset / 4;
|
|
int bitOffset = offset % 4;
|
|
data += byteOffset;
|
|
|
|
/* Bits to set start at bitOffset into data */
|
|
*value = getBits(data, val, 2, bitOffset);
|
|
break;
|
|
}
|
|
#endif
|
|
case PT_4BUI:
|
|
#ifdef OPTIMIZE_SPACE
|
|
{
|
|
int byteOffset = offset / 2;
|
|
int bitOffset = offset % 2;
|
|
data += byteOffset;
|
|
|
|
/* Bits to set start at bitOffset into data */
|
|
*value = getBits(data, val, 2, bitOffset);
|
|
break;
|
|
}
|
|
#endif
|
|
case PT_8BSI: {
|
|
int8_t val = data[offset];
|
|
*value = val;
|
|
break;
|
|
}
|
|
case PT_8BUI: {
|
|
uint8_t val = data[offset];
|
|
*value = val;
|
|
break;
|
|
}
|
|
case PT_16BSI: {
|
|
int16_t *ptr = (int16_t*) data; /* we assume correct alignment */
|
|
*value = ptr[offset];
|
|
break;
|
|
}
|
|
case PT_16BUI: {
|
|
uint16_t *ptr = (uint16_t*) data; /* we assume correct alignment */
|
|
*value = ptr[offset];
|
|
break;
|
|
}
|
|
case PT_32BSI: {
|
|
int32_t *ptr = (int32_t*) data; /* we assume correct alignment */
|
|
*value = ptr[offset];
|
|
break;
|
|
}
|
|
case PT_32BUI: {
|
|
uint32_t *ptr = (uint32_t*) data; /* we assume correct alignment */
|
|
*value = ptr[offset];
|
|
break;
|
|
}
|
|
case PT_32BF: {
|
|
float *ptr = (float*) data; /* we assume correct alignment */
|
|
*value = ptr[offset];
|
|
break;
|
|
}
|
|
case PT_64BF: {
|
|
double *ptr = (double*) data; /* we assume correct alignment */
|
|
*value = ptr[offset];
|
|
break;
|
|
}
|
|
default: {
|
|
rterror("rt_band_get_pixel: Unknown pixeltype %d", pixtype);
|
|
return ES_ERROR;
|
|
}
|
|
}
|
|
|
|
/* set NODATA flag */
|
|
if (band->hasnodata && nodata != NULL) {
|
|
if (rt_band_clamped_value_is_nodata(band, *value))
|
|
*nodata = 1;
|
|
}
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/**
|
|
* Get nearest pixel(s) with value (not NODATA) to specified pixel
|
|
*
|
|
* @param band : the band to get nearest pixel(s) from
|
|
* @param x : the column of the pixel (0-based)
|
|
* @param y : the line of the pixel (0-based)
|
|
* @param distancex : the number of pixels around the specified pixel
|
|
* along the X axis
|
|
* @param distancey : the number of pixels around the specified pixel
|
|
* along the Y axis
|
|
* @param exclude_nodata_value : if non-zero, ignore nodata values
|
|
* to check for pixels with value
|
|
* @param npixels : return set of rt_pixel object or NULL
|
|
*
|
|
* @return -1 on error, otherwise the number of rt_pixel objects
|
|
* in npixels
|
|
*/
|
|
int rt_band_get_nearest_pixel(
|
|
rt_band band,
|
|
int x, int y,
|
|
uint16_t distancex, uint16_t distancey,
|
|
int exclude_nodata_value,
|
|
rt_pixel *npixels
|
|
) {
|
|
rt_pixel npixel = NULL;
|
|
int extent[4] = {0};
|
|
int max_extent[4] = {0};
|
|
int d0 = 0;
|
|
int distance[2] = {0};
|
|
uint32_t _d[2] = {0};
|
|
uint32_t i = 0;
|
|
uint32_t j = 0;
|
|
uint32_t k = 0;
|
|
int _max = 0;
|
|
int _x = 0;
|
|
int _y = 0;
|
|
int *_min = NULL;
|
|
double pixval = 0;
|
|
double minval = 0;
|
|
uint32_t count = 0;
|
|
int isnodata = 0;
|
|
|
|
int inextent = 0;
|
|
|
|
assert(NULL != band);
|
|
assert(NULL != npixels);
|
|
|
|
RASTER_DEBUG(3, "Starting");
|
|
|
|
/* process distance */
|
|
distance[0] = distancex;
|
|
distance[1] = distancey;
|
|
|
|
/* no distance, means get nearest pixels and return */
|
|
if (!distance[0] && !distance[1])
|
|
d0 = 1;
|
|
|
|
RASTER_DEBUGF(4, "Selected pixel: %d x %d", x, y);
|
|
RASTER_DEBUGF(4, "Distances: %d x %d", distance[0], distance[1]);
|
|
|
|
/* shortcuts if outside band extent */
|
|
if (
|
|
exclude_nodata_value && (
|
|
(x < 0 || x > band->width) ||
|
|
(y < 0 || y > band->height)
|
|
)
|
|
) {
|
|
/* no distances specified, jump to pixel close to extent */
|
|
if (d0) {
|
|
if (x < 0)
|
|
x = -1;
|
|
else if (x > band->width)
|
|
x = band->width;
|
|
|
|
if (y < 0)
|
|
y = -1;
|
|
else if (y > band->height)
|
|
y = band->height;
|
|
|
|
RASTER_DEBUGF(4, "Moved selected pixel: %d x %d", x, y);
|
|
}
|
|
/*
|
|
distances specified
|
|
if distances won't capture extent of band, return 0
|
|
*/
|
|
else if (
|
|
((x < 0 && abs(x) > distance[0]) || (x - band->width >= distance[0])) ||
|
|
((y < 0 && abs(y) > distance[1]) || (y - band->height >= distance[1]))
|
|
) {
|
|
RASTER_DEBUG(4, "No nearest pixels possible for provided pixel and distances");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* no NODATA, exclude is FALSE */
|
|
if (!band->hasnodata)
|
|
exclude_nodata_value = FALSE;
|
|
/* band is NODATA and excluding NODATA */
|
|
else if (exclude_nodata_value && band->isnodata) {
|
|
RASTER_DEBUG(4, "No nearest pixels possible as band is NODATA and excluding NODATA values");
|
|
return 0;
|
|
}
|
|
|
|
/* determine the maximum distance to prevent an infinite loop */
|
|
if (d0) {
|
|
int a, b;
|
|
|
|
/* X axis */
|
|
a = abs(x);
|
|
b = abs(x - band->width);
|
|
|
|
if (a > b)
|
|
distance[0] = a;
|
|
else
|
|
distance[0] = b;
|
|
|
|
/* Y axis */
|
|
a = abs(y);
|
|
b = abs(y - band->height);
|
|
if (a > b)
|
|
distance[1] = a;
|
|
else
|
|
distance[1] = b;
|
|
|
|
RASTER_DEBUGF(4, "Maximum distances: %d x %d", distance[0], distance[1]);
|
|
}
|
|
|
|
/* minimum possible value for pixel type */
|
|
minval = rt_pixtype_get_min_value(band->pixtype);
|
|
RASTER_DEBUGF(4, "pixtype: %s", rt_pixtype_name(band->pixtype));
|
|
RASTER_DEBUGF(4, "minval: %f", minval);
|
|
|
|
/* set variables */
|
|
count = 0;
|
|
*npixels = NULL;
|
|
|
|
/* maximum extent */
|
|
max_extent[0] = x - distance[0]; /* min X */
|
|
max_extent[1] = y - distance[1]; /* min Y */
|
|
max_extent[2] = x + distance[0]; /* max X */
|
|
max_extent[3] = y + distance[1]; /* max Y */
|
|
RASTER_DEBUGF(4, "Maximum Extent: (%d, %d, %d, %d)",
|
|
max_extent[0], max_extent[1], max_extent[2], max_extent[3]);
|
|
|
|
_d[0] = 0;
|
|
_d[1] = 0;
|
|
do {
|
|
_d[0]++;
|
|
_d[1]++;
|
|
|
|
extent[0] = x - _d[0]; /* min x */
|
|
extent[1] = y - _d[1]; /* min y */
|
|
extent[2] = x + _d[0]; /* max x */
|
|
extent[3] = y + _d[1]; /* max y */
|
|
|
|
RASTER_DEBUGF(4, "Processing distances: %d x %d", _d[0], _d[1]);
|
|
RASTER_DEBUGF(4, "Extent: (%d, %d, %d, %d)",
|
|
extent[0], extent[1], extent[2], extent[3]);
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
/* by row */
|
|
if (i < 1)
|
|
_max = extent[2] - extent[0] + 1;
|
|
/* by column */
|
|
else
|
|
_max = extent[3] - extent[1] + 1;
|
|
_max = abs(_max);
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
/* by row */
|
|
if (i < 1) {
|
|
_x = extent[0];
|
|
_min = &_x;
|
|
|
|
/* top row */
|
|
if (j < 1)
|
|
_y = extent[1];
|
|
/* bottom row */
|
|
else
|
|
_y = extent[3];
|
|
}
|
|
/* by column */
|
|
else {
|
|
_y = extent[1] + 1;
|
|
_min = &_y;
|
|
|
|
/* left column */
|
|
if (j < 1) {
|
|
_x = extent[0];
|
|
_max -= 2;
|
|
}
|
|
/* right column */
|
|
else
|
|
_x = extent[2];
|
|
}
|
|
|
|
RASTER_DEBUGF(4, "_min, _max: %d, %d", *_min, _max);
|
|
for (k = 0; k < _max; k++) {
|
|
/* check that _x and _y are not outside max extent */
|
|
if (
|
|
_x < max_extent[0] || _x > max_extent[2] ||
|
|
_y < max_extent[1] || _y > max_extent[3]
|
|
) {
|
|
(*_min)++;
|
|
continue;
|
|
}
|
|
|
|
/* outside band extent, set to NODATA */
|
|
if (
|
|
(_x < 0 || _x >= band->width) ||
|
|
(_y < 0 || _y >= band->height)
|
|
) {
|
|
/* no NODATA, set to minimum possible value */
|
|
if (!band->hasnodata)
|
|
pixval = minval;
|
|
/* has NODATA, use NODATA */
|
|
else
|
|
pixval = band->nodataval;
|
|
RASTER_DEBUGF(4, "NODATA pixel outside band extent: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
|
|
inextent = 0;
|
|
isnodata = 1;
|
|
}
|
|
else {
|
|
if (rt_band_get_pixel(
|
|
band,
|
|
_x, _y,
|
|
&pixval,
|
|
&isnodata
|
|
) != ES_NONE) {
|
|
rterror("rt_band_get_nearest_pixel: Could not get pixel value");
|
|
if (count) rtdealloc(*npixels);
|
|
return -1;
|
|
}
|
|
RASTER_DEBUGF(4, "Pixel: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
|
|
inextent = 1;
|
|
}
|
|
|
|
/* use pixval? */
|
|
if (!exclude_nodata_value || (exclude_nodata_value && !isnodata)) {
|
|
/* add pixel to result set */
|
|
RASTER_DEBUGF(4, "Adding pixel to set of nearest pixels: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
|
|
count++;
|
|
|
|
if (*npixels == NULL)
|
|
*npixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t) * count);
|
|
else
|
|
*npixels = (rt_pixel) rtrealloc(*npixels, sizeof(struct rt_pixel_t) * count);
|
|
if (*npixels == NULL) {
|
|
rterror("rt_band_get_nearest_pixel: Could not allocate memory for nearest pixel(s)");
|
|
return -1;
|
|
}
|
|
|
|
npixel = &((*npixels)[count - 1]);
|
|
npixel->x = _x;
|
|
npixel->y = _y;
|
|
npixel->value = pixval;
|
|
|
|
/* special case for when outside band extent */
|
|
if (!inextent && !band->hasnodata)
|
|
npixel->nodata = 1;
|
|
else
|
|
npixel->nodata = 0;
|
|
}
|
|
|
|
(*_min)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* distance threshholds met */
|
|
if (_d[0] >= distance[0] && _d[1] >= distance[1])
|
|
break;
|
|
else if (d0 && count)
|
|
break;
|
|
}
|
|
while (1);
|
|
|
|
RASTER_DEBUGF(3, "Nearest pixels in return: %d", count);
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Search band for pixel(s) with search values
|
|
*
|
|
* @param band : the band to query for minimum and maximum pixel values
|
|
* @param exclude_nodata_value : if non-zero, ignore nodata values
|
|
* @param searchset : array of values to count
|
|
* @param searchcount : the number of search values
|
|
* @param pixels : pixels with the search value
|
|
*
|
|
* @return -1 on error, otherwise number of pixels
|
|
*/
|
|
int
|
|
rt_band_get_pixel_of_value(
|
|
rt_band band, int exclude_nodata_value,
|
|
double *searchset, int searchcount,
|
|
rt_pixel *pixels
|
|
) {
|
|
int x;
|
|
int y;
|
|
int i;
|
|
double pixval;
|
|
int err;
|
|
int count = 0;
|
|
int isnodata = 0;
|
|
int isequal = 0;
|
|
|
|
rt_pixel pixel = NULL;
|
|
|
|
assert(NULL != band);
|
|
assert(NULL != pixels);
|
|
assert(NULL != searchset && searchcount > 0);
|
|
|
|
if (!band->hasnodata)
|
|
exclude_nodata_value = FALSE;
|
|
/* band is NODATA and exclude_nodata_value = TRUE, nothing to search */
|
|
else if (exclude_nodata_value && band->isnodata) {
|
|
RASTER_DEBUG(4, "Pixels cannot be searched as band is NODATA and excluding NODATA values");
|
|
return 0;
|
|
}
|
|
|
|
for (x = 0; x < band->width; x++) {
|
|
for (y = 0; y < band->height; y++) {
|
|
err = rt_band_get_pixel(band, x, y, &pixval, &isnodata);
|
|
if (err != ES_NONE) {
|
|
rterror("rt_band_get_pixel_of_value: Cannot get band pixel");
|
|
return -1;
|
|
}
|
|
else if (exclude_nodata_value && isnodata)
|
|
continue;
|
|
|
|
for (i = 0; i < searchcount; i++) {
|
|
if (rt_pixtype_compare_clamped_values(band->pixtype, searchset[i], pixval, &isequal) != ES_NONE) {
|
|
continue;
|
|
}
|
|
|
|
if (FLT_NEQ(pixval, searchset[i]) || !isequal)
|
|
continue;
|
|
|
|
/* match found */
|
|
count++;
|
|
if (*pixels == NULL)
|
|
*pixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t) * count);
|
|
else
|
|
*pixels = (rt_pixel) rtrealloc(*pixels, sizeof(struct rt_pixel_t) * count);
|
|
if (*pixels == NULL) {
|
|
rterror("rt_band_get_pixel_of_value: Could not allocate memory for pixel(s)");
|
|
return -1;
|
|
}
|
|
|
|
pixel = &((*pixels)[count - 1]);
|
|
pixel->x = x;
|
|
pixel->y = y;
|
|
pixel->nodata = 0;
|
|
pixel->value = pixval;
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Get NODATA value
|
|
*
|
|
* @param band : the band whose NODATA value will be returned
|
|
* @param nodata : the band's NODATA value
|
|
*
|
|
* @return ES_NONE or ES_ERROR
|
|
*/
|
|
rt_errorstate
|
|
rt_band_get_nodata(rt_band band, double *nodata) {
|
|
assert(NULL != band);
|
|
assert(NULL != nodata);
|
|
|
|
*nodata = band->nodataval;
|
|
|
|
if (!band->hasnodata) {
|
|
rterror("rt_band_get_nodata: Band has no NODATA value");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
double
|
|
rt_band_get_min_value(rt_band band) {
|
|
assert(NULL != band);
|
|
|
|
return rt_pixtype_get_min_value(band->pixtype);
|
|
}
|
|
|
|
int
|
|
rt_band_check_is_nodata(rt_band band) {
|
|
int i, j, err;
|
|
double pxValue;
|
|
int isnodata = 0;
|
|
|
|
assert(NULL != band);
|
|
|
|
/* Check if band has nodata value */
|
|
if (!band->hasnodata) {
|
|
RASTER_DEBUG(3, "Band has no NODATA value");
|
|
band->isnodata = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
pxValue = band->nodataval;
|
|
|
|
/* Check all pixels */
|
|
for (i = 0; i < band->width; i++) {
|
|
for (j = 0; j < band->height; j++) {
|
|
err = rt_band_get_pixel(band, i, j, &pxValue, &isnodata);
|
|
if (err != ES_NONE) {
|
|
rterror("rt_band_check_is_nodata: Cannot get band pixel");
|
|
return FALSE;
|
|
}
|
|
else if (!isnodata) {
|
|
band->isnodata = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
band->isnodata = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Compare clamped value to band's clamped NODATA value.
|
|
*
|
|
* @param band : the band whose NODATA value will be used for comparison
|
|
* @param val : the value to compare to the NODATA value
|
|
*
|
|
* @return 2 if unclamped value is unclamped NODATA
|
|
* 1 if clamped value is clamped NODATA
|
|
* 0 if clamped value is NOT clamped NODATA
|
|
*/
|
|
int
|
|
rt_band_clamped_value_is_nodata(rt_band band, double val) {
|
|
int isequal = 0;
|
|
|
|
assert(NULL != band);
|
|
|
|
/* no NODATA, so never equal */
|
|
if (!band->hasnodata)
|
|
return 0;
|
|
|
|
/* value is exactly NODATA */
|
|
if (FLT_EQ(val, band->nodataval))
|
|
return 2;
|
|
|
|
/* ignore error from rt_pixtype_compare_clamped_values */
|
|
rt_pixtype_compare_clamped_values(
|
|
band->pixtype,
|
|
val, band->nodataval,
|
|
&isequal
|
|
);
|
|
|
|
return isequal ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Correct value when clamped value is equal to clamped NODATA value.
|
|
* Correction does NOT occur if unclamped value is exactly unclamped
|
|
* NODATA value.
|
|
*
|
|
* @param band : the band whose NODATA value will be used for comparison
|
|
* @param val : the value to compare to the NODATA value and correct
|
|
* @param *newval : pointer to corrected value
|
|
* @param *corrected : (optional) non-zero if val was corrected
|
|
*
|
|
* @return ES_NONE if success, ES_ERROR if error
|
|
*/
|
|
rt_errorstate
|
|
rt_band_corrected_clamped_value(
|
|
rt_band band,
|
|
double val,
|
|
double *newval, int *corrected
|
|
) {
|
|
double minval = 0.;
|
|
|
|
assert(NULL != band);
|
|
assert(NULL != newval);
|
|
|
|
if (corrected != NULL)
|
|
*corrected = 0;
|
|
|
|
/* no need to correct if clamped values IS NOT clamped NODATA */
|
|
if (rt_band_clamped_value_is_nodata(band, val) != 1) {
|
|
*newval = val;
|
|
return ES_NONE;
|
|
}
|
|
|
|
minval = rt_pixtype_get_min_value(band->pixtype);
|
|
*newval = val;
|
|
|
|
switch (band->pixtype) {
|
|
case PT_1BB:
|
|
*newval = !band->nodataval;
|
|
break;
|
|
case PT_2BUI:
|
|
if (rt_util_clamp_to_2BUI(val) == rt_util_clamp_to_2BUI(minval))
|
|
(*newval)++;
|
|
else
|
|
(*newval)--;
|
|
break;
|
|
case PT_4BUI:
|
|
if (rt_util_clamp_to_4BUI(val) == rt_util_clamp_to_4BUI(minval))
|
|
(*newval)++;
|
|
else
|
|
(*newval)--;
|
|
break;
|
|
case PT_8BSI:
|
|
if (rt_util_clamp_to_8BSI(val) == rt_util_clamp_to_8BSI(minval))
|
|
(*newval)++;
|
|
else
|
|
(*newval)--;
|
|
break;
|
|
case PT_8BUI:
|
|
if (rt_util_clamp_to_8BUI(val) == rt_util_clamp_to_8BUI(minval))
|
|
(*newval)++;
|
|
else
|
|
(*newval)--;
|
|
break;
|
|
case PT_16BSI:
|
|
if (rt_util_clamp_to_16BSI(val) == rt_util_clamp_to_16BSI(minval))
|
|
(*newval)++;
|
|
else
|
|
(*newval)--;
|
|
break;
|
|
case PT_16BUI:
|
|
if (rt_util_clamp_to_16BUI(val) == rt_util_clamp_to_16BUI(minval))
|
|
(*newval)++;
|
|
else
|
|
(*newval)--;
|
|
break;
|
|
case PT_32BSI:
|
|
if (rt_util_clamp_to_32BSI(val) == rt_util_clamp_to_32BSI(minval))
|
|
(*newval)++;
|
|
else
|
|
(*newval)--;
|
|
break;
|
|
case PT_32BUI:
|
|
if (rt_util_clamp_to_32BUI(val) == rt_util_clamp_to_32BUI(minval))
|
|
(*newval)++;
|
|
else
|
|
(*newval)--;
|
|
break;
|
|
case PT_32BF:
|
|
if (FLT_EQ(rt_util_clamp_to_32F(val), rt_util_clamp_to_32F(minval)))
|
|
*newval += FLT_EPSILON;
|
|
else
|
|
*newval -= FLT_EPSILON;
|
|
break;
|
|
case PT_64BF:
|
|
break;
|
|
default:
|
|
rterror("rt_band_corrected_clamped_value: Unknown pixeltype %d", band->pixtype);
|
|
return ES_ERROR;
|
|
}
|
|
|
|
if (corrected != NULL)
|
|
*corrected = 1;
|
|
|
|
return ES_NONE;
|
|
}
|
|
|