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
3734 lines
95 KiB
C
3734 lines
95 KiB
C
/*
|
|
*
|
|
* WKTRaster - Raster Types for PostGIS
|
|
* http://trac.osgeo.org/postgis/wiki/WKTRaster
|
|
*
|
|
* Copyright (C) 2013 Bborie Park <dustymugs@gmail.com>
|
|
* 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 "librtcore.h"
|
|
#include "librtcore_internal.h"
|
|
|
|
#include <math.h>
|
|
|
|
/**
|
|
* Construct a raster with given dimensions.
|
|
*
|
|
* Transform will be set to identity.
|
|
* Will contain no bands.
|
|
*
|
|
* @param width : number of pixel columns
|
|
* @param height : number of pixel rows
|
|
*
|
|
* @return an rt_raster or NULL if out of memory
|
|
*/
|
|
rt_raster
|
|
rt_raster_new(uint32_t width, uint32_t height) {
|
|
rt_raster ret = NULL;
|
|
|
|
ret = (rt_raster) rtalloc(sizeof (struct rt_raster_t));
|
|
if (!ret) {
|
|
rterror("rt_raster_new: Out of virtual memory creating an rt_raster");
|
|
return NULL;
|
|
}
|
|
|
|
RASTER_DEBUGF(3, "Created rt_raster @ %p", ret);
|
|
|
|
if (width > 65535 || height > 65535) {
|
|
rterror("rt_raster_new: Dimensions requested exceed the maximum (65535 x 65535) permitted for a raster");
|
|
rt_raster_destroy(ret);
|
|
return NULL;
|
|
}
|
|
|
|
ret->width = width;
|
|
ret->height = height;
|
|
ret->scaleX = 1;
|
|
ret->scaleY = -1;
|
|
ret->ipX = 0.0;
|
|
ret->ipY = 0.0;
|
|
ret->skewX = 0.0;
|
|
ret->skewY = 0.0;
|
|
ret->srid = SRID_UNKNOWN;
|
|
|
|
ret->numBands = 0;
|
|
ret->bands = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
rt_raster_destroy(rt_raster raster) {
|
|
if (raster == NULL)
|
|
return;
|
|
|
|
RASTER_DEBUGF(3, "Destroying rt_raster @ %p", raster);
|
|
|
|
if (raster->bands)
|
|
rtdealloc(raster->bands);
|
|
|
|
rtdealloc(raster);
|
|
}
|
|
|
|
static void
|
|
_rt_raster_geotransform_warn_offline_band(rt_raster raster) {
|
|
int numband = 0;
|
|
int i = 0;
|
|
rt_band band = NULL;
|
|
|
|
if (raster == NULL)
|
|
return;
|
|
|
|
numband = rt_raster_get_num_bands(raster);
|
|
if (numband < 1)
|
|
return;
|
|
|
|
for (i = 0; i < numband; i++) {
|
|
band = rt_raster_get_band(raster, i);
|
|
if (NULL == band)
|
|
continue;
|
|
|
|
if (!rt_band_is_offline(band))
|
|
continue;
|
|
|
|
rtwarn("Changes made to raster geotransform matrix may affect out-db band data. Returned band data may be incorrect");
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint16_t
|
|
rt_raster_get_width(rt_raster raster) {
|
|
|
|
assert(NULL != raster);
|
|
|
|
return raster->width;
|
|
}
|
|
|
|
uint16_t
|
|
rt_raster_get_height(rt_raster raster) {
|
|
|
|
assert(NULL != raster);
|
|
|
|
return raster->height;
|
|
}
|
|
|
|
void
|
|
rt_raster_set_scale(
|
|
rt_raster raster,
|
|
double scaleX, double scaleY
|
|
) {
|
|
assert(NULL != raster);
|
|
|
|
raster->scaleX = scaleX;
|
|
raster->scaleY = scaleY;
|
|
|
|
_rt_raster_geotransform_warn_offline_band(raster);
|
|
}
|
|
|
|
double
|
|
rt_raster_get_x_scale(rt_raster raster) {
|
|
|
|
|
|
assert(NULL != raster);
|
|
|
|
return raster->scaleX;
|
|
}
|
|
|
|
double
|
|
rt_raster_get_y_scale(rt_raster raster) {
|
|
|
|
|
|
assert(NULL != raster);
|
|
|
|
return raster->scaleY;
|
|
}
|
|
|
|
void
|
|
rt_raster_set_skews(
|
|
rt_raster raster,
|
|
double skewX, double skewY
|
|
) {
|
|
assert(NULL != raster);
|
|
|
|
raster->skewX = skewX;
|
|
raster->skewY = skewY;
|
|
|
|
_rt_raster_geotransform_warn_offline_band(raster);
|
|
}
|
|
|
|
double
|
|
rt_raster_get_x_skew(rt_raster raster) {
|
|
|
|
|
|
assert(NULL != raster);
|
|
|
|
return raster->skewX;
|
|
}
|
|
|
|
double
|
|
rt_raster_get_y_skew(rt_raster raster) {
|
|
|
|
|
|
assert(NULL != raster);
|
|
|
|
return raster->skewY;
|
|
}
|
|
|
|
void
|
|
rt_raster_set_offsets(
|
|
rt_raster raster,
|
|
double x, double y
|
|
) {
|
|
|
|
assert(NULL != raster);
|
|
|
|
raster->ipX = x;
|
|
raster->ipY = y;
|
|
|
|
_rt_raster_geotransform_warn_offline_band(raster);
|
|
}
|
|
|
|
double
|
|
rt_raster_get_x_offset(rt_raster raster) {
|
|
|
|
|
|
assert(NULL != raster);
|
|
|
|
return raster->ipX;
|
|
}
|
|
|
|
double
|
|
rt_raster_get_y_offset(rt_raster raster) {
|
|
|
|
|
|
assert(NULL != raster);
|
|
|
|
return raster->ipY;
|
|
}
|
|
|
|
void
|
|
rt_raster_get_phys_params(rt_raster rast,
|
|
double *i_mag, double *j_mag, double *theta_i, double *theta_ij)
|
|
{
|
|
double o11, o12, o21, o22 ; /* geotransform coefficients */
|
|
|
|
if (rast == NULL) return ;
|
|
if ( (i_mag==NULL) || (j_mag==NULL) || (theta_i==NULL) || (theta_ij==NULL))
|
|
return ;
|
|
|
|
/* retrieve coefficients from raster */
|
|
o11 = rt_raster_get_x_scale(rast) ;
|
|
o12 = rt_raster_get_x_skew(rast) ;
|
|
o21 = rt_raster_get_y_skew(rast) ;
|
|
o22 = rt_raster_get_y_scale(rast) ;
|
|
|
|
rt_raster_calc_phys_params(o11, o12, o21, o22, i_mag, j_mag, theta_i, theta_ij);
|
|
}
|
|
|
|
void
|
|
rt_raster_calc_phys_params(double xscale, double xskew, double yskew, double yscale,
|
|
double *i_mag, double *j_mag, double *theta_i, double *theta_ij)
|
|
|
|
{
|
|
double theta_test ;
|
|
|
|
if ( (i_mag==NULL) || (j_mag==NULL) || (theta_i==NULL) || (theta_ij==NULL))
|
|
return ;
|
|
|
|
/* pixel size in the i direction */
|
|
*i_mag = sqrt(xscale*xscale + yskew*yskew) ;
|
|
|
|
/* pixel size in the j direction */
|
|
*j_mag = sqrt(xskew*xskew + yscale*yscale) ;
|
|
|
|
/* Rotation
|
|
* ========
|
|
* Two steps:
|
|
* 1] calculate the magnitude of the angle between the x axis and
|
|
* the i basis vector.
|
|
* 2] Calculate the sign of theta_i based on the angle between the y axis
|
|
* and the i basis vector.
|
|
*/
|
|
*theta_i = acos(xscale/(*i_mag)) ; /* magnitude */
|
|
theta_test = acos(yskew/(*i_mag)) ; /* sign */
|
|
if (theta_test < M_PI_2){
|
|
*theta_i = -(*theta_i) ;
|
|
}
|
|
|
|
|
|
/* Angular separation of basis vectors
|
|
* ===================================
|
|
* Two steps:
|
|
* 1] calculate the magnitude of the angle between the j basis vector and
|
|
* the i basis vector.
|
|
* 2] Calculate the sign of theta_ij based on the angle between the
|
|
* perpendicular of the i basis vector and the j basis vector.
|
|
*/
|
|
*theta_ij = acos(((xscale*xskew) + (yskew*yscale))/((*i_mag)*(*j_mag))) ;
|
|
theta_test = acos( ((-yskew*xskew)+(xscale*yscale)) /
|
|
((*i_mag)*(*j_mag)));
|
|
if (theta_test > M_PI_2) {
|
|
*theta_ij = -(*theta_ij) ;
|
|
}
|
|
}
|
|
|
|
void
|
|
rt_raster_set_phys_params(rt_raster rast,double i_mag, double j_mag, double theta_i, double theta_ij)
|
|
{
|
|
double o11, o12, o21, o22 ; /* calculated geotransform coefficients */
|
|
int success ;
|
|
|
|
if (rast == NULL) return ;
|
|
|
|
success = rt_raster_calc_gt_coeff(i_mag, j_mag, theta_i, theta_ij,
|
|
&o11, &o12, &o21, &o22) ;
|
|
|
|
if (success) {
|
|
rt_raster_set_scale(rast, o11, o22) ;
|
|
rt_raster_set_skews(rast, o12, o21) ;
|
|
}
|
|
}
|
|
|
|
int
|
|
rt_raster_calc_gt_coeff(double i_mag, double j_mag, double theta_i, double theta_ij,
|
|
double *xscale, double *xskew, double *yskew, double *yscale)
|
|
{
|
|
double f ; /* reflection flag 1.0 or -1.0 */
|
|
double k_i ; /* shearing coefficient */
|
|
double s_i, s_j ; /* scaling coefficients */
|
|
double cos_theta_i, sin_theta_i ;
|
|
|
|
if ( (xscale==NULL) || (xskew==NULL) || (yskew==NULL) || (yscale==NULL)) {
|
|
return 0;
|
|
}
|
|
|
|
if ( (theta_ij == 0.0) || (theta_ij == M_PI)) {
|
|
return 0;
|
|
}
|
|
|
|
/* Reflection across the i axis */
|
|
f=1.0 ;
|
|
if (theta_ij < 0) {
|
|
f = -1.0;
|
|
}
|
|
|
|
/* scaling along i axis */
|
|
s_i = i_mag ;
|
|
|
|
/* shearing parallel to i axis */
|
|
k_i = tan(f*M_PI_2 - theta_ij) ;
|
|
|
|
/* scaling along j axis */
|
|
s_j = j_mag / (sqrt(k_i*k_i + 1)) ;
|
|
|
|
/* putting it altogether */
|
|
cos_theta_i = cos(theta_i) ;
|
|
sin_theta_i = sin(theta_i) ;
|
|
*xscale = s_i * cos_theta_i ;
|
|
*xskew = k_i * s_j * f * cos_theta_i + s_j * f * sin_theta_i ;
|
|
*yskew = -s_i * sin_theta_i ;
|
|
*yscale = -k_i * s_j * f * sin_theta_i + s_j * f * cos_theta_i ;
|
|
return 1;
|
|
}
|
|
|
|
int32_t
|
|
rt_raster_get_srid(rt_raster raster) {
|
|
assert(NULL != raster);
|
|
|
|
return clamp_srid(raster->srid);
|
|
}
|
|
|
|
void
|
|
rt_raster_set_srid(rt_raster raster, int32_t srid) {
|
|
assert(NULL != raster);
|
|
|
|
raster->srid = clamp_srid(srid);
|
|
|
|
_rt_raster_geotransform_warn_offline_band(raster);
|
|
}
|
|
|
|
int
|
|
rt_raster_get_num_bands(rt_raster raster) {
|
|
|
|
|
|
assert(NULL != raster);
|
|
|
|
return raster->numBands;
|
|
}
|
|
|
|
rt_band
|
|
rt_raster_get_band(rt_raster raster, int n) {
|
|
assert(NULL != raster);
|
|
|
|
if (n >= raster->numBands || n < 0)
|
|
return NULL;
|
|
|
|
return raster->bands[n];
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_add_band()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Add band data to a raster.
|
|
*
|
|
* @param raster : the raster to add a band to
|
|
* @param band : the band to add, ownership left to caller.
|
|
* Band dimensions are required to match with raster ones.
|
|
* @param index : the position where to insert the new band (0 based)
|
|
*
|
|
* @return identifier (position) for the just-added raster, or -1 on error
|
|
*/
|
|
int
|
|
rt_raster_add_band(rt_raster raster, rt_band band, int index) {
|
|
rt_band *oldbands = NULL;
|
|
rt_band oldband = NULL;
|
|
rt_band tmpband = NULL;
|
|
uint16_t i = 0;
|
|
|
|
assert(NULL != raster);
|
|
assert(NULL != band);
|
|
|
|
RASTER_DEBUGF(3, "Adding band %p to raster %p", band, raster);
|
|
|
|
if (band->width != raster->width || band->height != raster->height) {
|
|
rterror("rt_raster_add_band: Can't add a %dx%d band to a %dx%d raster",
|
|
band->width, band->height, raster->width, raster->height);
|
|
return -1;
|
|
}
|
|
|
|
if (index > raster->numBands)
|
|
index = raster->numBands;
|
|
|
|
if (index < 0)
|
|
index = 0;
|
|
|
|
oldbands = raster->bands;
|
|
|
|
RASTER_DEBUGF(3, "Oldbands at %p", oldbands);
|
|
|
|
raster->bands = (rt_band*) rtrealloc(raster->bands,
|
|
sizeof (rt_band)*(raster->numBands + 1)
|
|
);
|
|
|
|
RASTER_DEBUG(3, "Checking bands");
|
|
|
|
if (NULL == raster->bands) {
|
|
rterror("rt_raster_add_band: Out of virtual memory "
|
|
"reallocating band pointers");
|
|
raster->bands = oldbands;
|
|
return -1;
|
|
}
|
|
|
|
RASTER_DEBUGF(4, "realloc returned %p", raster->bands);
|
|
|
|
for (i = 0; i <= raster->numBands; ++i) {
|
|
if (i == index) {
|
|
oldband = raster->bands[i];
|
|
raster->bands[i] = band;
|
|
} else if (i > index) {
|
|
tmpband = raster->bands[i];
|
|
raster->bands[i] = oldband;
|
|
oldband = tmpband;
|
|
}
|
|
}
|
|
|
|
band->raster = raster;
|
|
|
|
raster->numBands++;
|
|
|
|
RASTER_DEBUGF(4, "Raster now has %d bands", raster->numBands);
|
|
|
|
return index;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_generate_new_band()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Generate a new inline band and add it to a raster.
|
|
* Memory is allocated in this function for band data.
|
|
*
|
|
* @param raster : the raster to add a band to
|
|
* @param pixtype : the pixel type for the new band
|
|
* @param initialvalue : initial value for pixels
|
|
* @param hasnodata : indicates if the band has a nodata value
|
|
* @param nodatavalue : nodata value for the new band
|
|
* @param index : position to add the new band in the raster
|
|
*
|
|
* @return identifier (position) for the just-added raster, or -1 on error
|
|
*/
|
|
int
|
|
rt_raster_generate_new_band(
|
|
rt_raster raster, rt_pixtype pixtype,
|
|
double initialvalue, uint32_t hasnodata, double nodatavalue,
|
|
int index
|
|
) {
|
|
rt_band band = NULL;
|
|
int width = 0;
|
|
int height = 0;
|
|
int numval = 0;
|
|
int datasize = 0;
|
|
int oldnumbands = 0;
|
|
int numbands = 0;
|
|
void * mem = NULL;
|
|
int32_t checkvalint = 0;
|
|
uint32_t checkvaluint = 0;
|
|
double checkvaldouble = 0;
|
|
float checkvalfloat = 0;
|
|
int i;
|
|
|
|
|
|
assert(NULL != raster);
|
|
|
|
/* Make sure index is in a valid range */
|
|
oldnumbands = rt_raster_get_num_bands(raster);
|
|
if (index < 0)
|
|
index = 0;
|
|
else if (index > oldnumbands + 1)
|
|
index = oldnumbands + 1;
|
|
|
|
/* Determine size of memory block to allocate and allocate it */
|
|
width = rt_raster_get_width(raster);
|
|
height = rt_raster_get_height(raster);
|
|
numval = width * height;
|
|
datasize = rt_pixtype_size(pixtype) * numval;
|
|
|
|
mem = (int *)rtalloc(datasize);
|
|
if (!mem) {
|
|
rterror("rt_raster_generate_new_band: Could not allocate memory for band");
|
|
return -1;
|
|
}
|
|
|
|
if (FLT_EQ(initialvalue, 0.0))
|
|
memset(mem, 0, datasize);
|
|
else {
|
|
switch (pixtype)
|
|
{
|
|
case PT_1BB:
|
|
{
|
|
uint8_t *ptr = mem;
|
|
uint8_t clamped_initval = rt_util_clamp_to_1BB(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvalint = ptr[0];
|
|
break;
|
|
}
|
|
case PT_2BUI:
|
|
{
|
|
uint8_t *ptr = mem;
|
|
uint8_t clamped_initval = rt_util_clamp_to_2BUI(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvalint = ptr[0];
|
|
break;
|
|
}
|
|
case PT_4BUI:
|
|
{
|
|
uint8_t *ptr = mem;
|
|
uint8_t clamped_initval = rt_util_clamp_to_4BUI(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvalint = ptr[0];
|
|
break;
|
|
}
|
|
case PT_8BSI:
|
|
{
|
|
int8_t *ptr = mem;
|
|
int8_t clamped_initval = rt_util_clamp_to_8BSI(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvalint = ptr[0];
|
|
break;
|
|
}
|
|
case PT_8BUI:
|
|
{
|
|
uint8_t *ptr = mem;
|
|
uint8_t clamped_initval = rt_util_clamp_to_8BUI(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvalint = ptr[0];
|
|
break;
|
|
}
|
|
case PT_16BSI:
|
|
{
|
|
int16_t *ptr = mem;
|
|
int16_t clamped_initval = rt_util_clamp_to_16BSI(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvalint = ptr[0];
|
|
break;
|
|
}
|
|
case PT_16BUI:
|
|
{
|
|
uint16_t *ptr = mem;
|
|
uint16_t clamped_initval = rt_util_clamp_to_16BUI(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvalint = ptr[0];
|
|
break;
|
|
}
|
|
case PT_32BSI:
|
|
{
|
|
int32_t *ptr = mem;
|
|
int32_t clamped_initval = rt_util_clamp_to_32BSI(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvalint = ptr[0];
|
|
break;
|
|
}
|
|
case PT_32BUI:
|
|
{
|
|
uint32_t *ptr = mem;
|
|
uint32_t clamped_initval = rt_util_clamp_to_32BUI(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvaluint = ptr[0];
|
|
break;
|
|
}
|
|
case PT_32BF:
|
|
{
|
|
float *ptr = mem;
|
|
float clamped_initval = rt_util_clamp_to_32F(initialvalue);
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = clamped_initval;
|
|
checkvalfloat = ptr[0];
|
|
break;
|
|
}
|
|
case PT_64BF:
|
|
{
|
|
double *ptr = mem;
|
|
for (i = 0; i < numval; i++)
|
|
ptr[i] = initialvalue;
|
|
checkvaldouble = ptr[0];
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
rterror("rt_raster_generate_new_band: Unknown pixeltype %d", pixtype);
|
|
rtdealloc(mem);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Overflow checking */
|
|
rt_util_dbl_trunc_warning(
|
|
initialvalue,
|
|
checkvalint, checkvaluint,
|
|
checkvalfloat, checkvaldouble,
|
|
pixtype
|
|
);
|
|
|
|
band = rt_band_new_inline(width, height, pixtype, hasnodata, nodatavalue, mem);
|
|
if (! band) {
|
|
rterror("rt_raster_generate_new_band: Could not add band to raster. Aborting");
|
|
rtdealloc(mem);
|
|
return -1;
|
|
}
|
|
rt_band_set_ownsdata_flag(band, 1); /* we DO own this data!!! */
|
|
index = rt_raster_add_band(raster, band, index);
|
|
numbands = rt_raster_get_num_bands(raster);
|
|
if (numbands == oldnumbands || index == -1) {
|
|
rterror("rt_raster_generate_new_band: Could not add band to raster. Aborting");
|
|
rt_band_destroy(band);
|
|
}
|
|
|
|
/* set isnodata if hasnodata = TRUE and initial value = nodatavalue */
|
|
if (hasnodata && FLT_EQ(initialvalue, nodatavalue))
|
|
rt_band_set_isnodata_flag(band, 1);
|
|
|
|
return index;
|
|
}
|
|
|
|
/**
|
|
* Get 6-element array of raster inverse geotransform matrix
|
|
*
|
|
* @param raster : the raster to get matrix of
|
|
* @param gt : optional input parameter, 6-element geotransform matrix
|
|
* @param igt : output parameter, 6-element inverse geotransform matrix
|
|
*
|
|
* @return ES_NONE if success, ES_ERROR if error
|
|
*/
|
|
rt_errorstate rt_raster_get_inverse_geotransform_matrix(
|
|
rt_raster raster,
|
|
double *gt, double *igt
|
|
) {
|
|
double _gt[6] = {0};
|
|
|
|
assert((raster != NULL || gt != NULL));
|
|
assert(igt != NULL);
|
|
|
|
if (gt == NULL)
|
|
rt_raster_get_geotransform_matrix(raster, _gt);
|
|
else
|
|
memcpy(_gt, gt, sizeof(double) * 6);
|
|
|
|
if (!GDALInvGeoTransform(_gt, igt)) {
|
|
rterror("rt_raster_get_inverse_geotransform_matrix: Could not compute inverse geotransform matrix");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/**
|
|
* Get 6-element array of raster geotransform matrix
|
|
*
|
|
* @param raster : the raster to get matrix of
|
|
* @param gt : output parameter, 6-element geotransform matrix
|
|
*
|
|
*/
|
|
void
|
|
rt_raster_get_geotransform_matrix(rt_raster raster,
|
|
double *gt) {
|
|
assert(NULL != raster);
|
|
assert(NULL != gt);
|
|
|
|
gt[0] = raster->ipX;
|
|
gt[1] = raster->scaleX;
|
|
gt[2] = raster->skewX;
|
|
gt[3] = raster->ipY;
|
|
gt[4] = raster->skewY;
|
|
gt[5] = raster->scaleY;
|
|
}
|
|
|
|
/**
|
|
* Set raster's geotransform using 6-element array
|
|
*
|
|
* @param raster : the raster to set matrix of
|
|
* @param gt : intput parameter, 6-element geotransform matrix
|
|
*
|
|
*/
|
|
void
|
|
rt_raster_set_geotransform_matrix(rt_raster raster,
|
|
double *gt) {
|
|
assert(NULL != raster);
|
|
assert(NULL != gt);
|
|
|
|
raster->ipX = gt[0];
|
|
raster->scaleX = gt[1];
|
|
raster->skewX = gt[2];
|
|
raster->ipY = gt[3];
|
|
raster->skewY = gt[4];
|
|
raster->scaleY = gt[5];
|
|
|
|
_rt_raster_geotransform_warn_offline_band(raster);
|
|
}
|
|
|
|
/**
|
|
* Convert an xr, yr raster point to an xw, yw point on map
|
|
*
|
|
* @param raster : the raster to get info from
|
|
* @param xr : the pixel's column
|
|
* @param yr : the pixel's row
|
|
* @param xw : output parameter, X ordinate of the geographical point
|
|
* @param yw : output parameter, Y ordinate of the geographical point
|
|
* @param gt : input/output parameter, 3x2 geotransform matrix
|
|
*
|
|
* @return ES_NONE if success, ES_ERROR if error
|
|
*/
|
|
rt_errorstate
|
|
rt_raster_cell_to_geopoint(
|
|
rt_raster raster,
|
|
double xr, double yr,
|
|
double *xw, double *yw,
|
|
double *gt
|
|
) {
|
|
double _gt[6] = {0};
|
|
|
|
assert(NULL != raster);
|
|
assert(NULL != xw && NULL != yw);
|
|
|
|
if (NULL != gt)
|
|
memcpy(_gt, gt, sizeof(double) * 6);
|
|
|
|
/* scale of matrix is not set */
|
|
if (
|
|
FLT_EQ(_gt[1], 0) ||
|
|
FLT_EQ(_gt[5], 0)
|
|
) {
|
|
rt_raster_get_geotransform_matrix(raster, _gt);
|
|
}
|
|
|
|
RASTER_DEBUGF(4, "gt = (%f, %f, %f, %f, %f, %f)",
|
|
_gt[0],
|
|
_gt[1],
|
|
_gt[2],
|
|
_gt[3],
|
|
_gt[4],
|
|
_gt[5]
|
|
);
|
|
|
|
GDALApplyGeoTransform(_gt, xr, yr, xw, yw);
|
|
RASTER_DEBUGF(4, "GDALApplyGeoTransform (c -> g) for (%f, %f) = (%f, %f)",
|
|
xr, yr, *xw, *yw);
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/**
|
|
* Convert an xw,yw map point to a xr,yr raster point
|
|
*
|
|
* @param raster : the raster to get info from
|
|
* @param xw : X ordinate of the geographical point
|
|
* @param yw : Y ordinate of the geographical point
|
|
* @param xr : output parameter, the pixel's column
|
|
* @param yr : output parameter, the pixel's row
|
|
* @param igt : input/output parameter, inverse geotransform matrix
|
|
*
|
|
* @return ES_NONE if success, ES_ERROR if error
|
|
*/
|
|
rt_errorstate
|
|
rt_raster_geopoint_to_cell(
|
|
rt_raster raster,
|
|
double xw, double yw,
|
|
double *xr, double *yr,
|
|
double *igt
|
|
) {
|
|
double _igt[6] = {0};
|
|
double rnd = 0;
|
|
|
|
assert(NULL != raster);
|
|
assert(NULL != xr && NULL != yr);
|
|
|
|
if (igt != NULL)
|
|
memcpy(_igt, igt, sizeof(double) * 6);
|
|
|
|
/* matrix is not set */
|
|
if (
|
|
FLT_EQ(_igt[0], 0.) &&
|
|
FLT_EQ(_igt[1], 0.) &&
|
|
FLT_EQ(_igt[2], 0.) &&
|
|
FLT_EQ(_igt[3], 0.) &&
|
|
FLT_EQ(_igt[4], 0.) &&
|
|
FLT_EQ(_igt[5], 0.)
|
|
) {
|
|
if (rt_raster_get_inverse_geotransform_matrix(raster, NULL, _igt) != ES_NONE) {
|
|
rterror("rt_raster_geopoint_to_cell: Could not get inverse geotransform matrix");
|
|
return ES_ERROR;
|
|
}
|
|
}
|
|
|
|
GDALApplyGeoTransform(_igt, xw, yw, xr, yr);
|
|
RASTER_DEBUGF(4, "GDALApplyGeoTransform (g -> c) for (%f, %f) = (%f, %f)",
|
|
xw, yw, *xr, *yr);
|
|
|
|
rnd = ROUND(*xr, 0);
|
|
if (FLT_EQ(rnd, *xr))
|
|
*xr = rnd;
|
|
else
|
|
*xr = floor(*xr);
|
|
|
|
rnd = ROUND(*yr, 0);
|
|
if (FLT_EQ(rnd, *yr))
|
|
*yr = rnd;
|
|
else
|
|
*yr = floor(*yr);
|
|
|
|
RASTER_DEBUGF(4, "Corrected GDALApplyGeoTransform (g -> c) for (%f, %f) = (%f, %f)",
|
|
xw, yw, *xr, *yr);
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_get_envelope()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Get raster's envelope.
|
|
*
|
|
* The envelope is the minimum bounding rectangle of the raster
|
|
*
|
|
* @param raster : the raster to get envelope of
|
|
* @param env : pointer to rt_envelope
|
|
*
|
|
* @return ES_NONE if success, ES_ERROR if error
|
|
*/
|
|
rt_errorstate
|
|
rt_raster_get_envelope(
|
|
rt_raster raster,
|
|
rt_envelope *env
|
|
) {
|
|
int i;
|
|
int rtn;
|
|
int set = 0;
|
|
double _r[2] = {0.};
|
|
double _w[2] = {0.};
|
|
double _gt[6] = {0.};
|
|
|
|
assert(raster != NULL);
|
|
assert(env != NULL);
|
|
|
|
rt_raster_get_geotransform_matrix(raster, _gt);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
switch (i) {
|
|
case 0:
|
|
_r[0] = 0;
|
|
_r[1] = 0;
|
|
break;
|
|
case 1:
|
|
_r[0] = 0;
|
|
_r[1] = raster->height;
|
|
break;
|
|
case 2:
|
|
_r[0] = raster->width;
|
|
_r[1] = raster->height;
|
|
break;
|
|
case 3:
|
|
_r[0] = raster->width;
|
|
_r[1] = 0;
|
|
break;
|
|
}
|
|
|
|
rtn = rt_raster_cell_to_geopoint(
|
|
raster,
|
|
_r[0], _r[1],
|
|
&(_w[0]), &(_w[1]),
|
|
_gt
|
|
);
|
|
if (rtn != ES_NONE) {
|
|
rterror("rt_raster_get_envelope: Could not compute spatial coordinates for raster pixel");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
if (!set) {
|
|
set = 1;
|
|
env->MinX = _w[0];
|
|
env->MaxX = _w[0];
|
|
env->MinY = _w[1];
|
|
env->MaxY = _w[1];
|
|
}
|
|
else {
|
|
if (_w[0] < env->MinX)
|
|
env->MinX = _w[0];
|
|
else if (_w[0] > env->MaxX)
|
|
env->MaxX = _w[0];
|
|
|
|
if (_w[1] < env->MinY)
|
|
env->MinY = _w[1];
|
|
else if (_w[1] > env->MaxY)
|
|
env->MaxY = _w[1];
|
|
}
|
|
}
|
|
|
|
return ES_NONE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_compute_skewed_raster()
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* Compute skewed extent that covers unskewed extent.
|
|
*
|
|
* @param envelope : unskewed extent of type rt_envelope
|
|
* @param skew : pointer to 2-element array (x, y) of skew
|
|
* @param scale : pointer to 2-element array (x, y) of scale
|
|
* @param tolerance : value between 0 and 1 where the smaller the tolerance
|
|
* results in an extent approaching the "minimum" skewed extent.
|
|
* If value <= 0, tolerance = 0.1. If value > 1, tolerance = 1.
|
|
*
|
|
* @return skewed raster who's extent covers unskewed extent, NULL on error
|
|
*/
|
|
rt_raster
|
|
rt_raster_compute_skewed_raster(
|
|
rt_envelope extent,
|
|
double *skew,
|
|
double *scale,
|
|
double tolerance
|
|
) {
|
|
uint32_t run = 0;
|
|
uint32_t max_run = 1;
|
|
double dbl_run = 0;
|
|
|
|
int rtn;
|
|
int covers = 0;
|
|
rt_raster raster;
|
|
double _gt[6] = {0};
|
|
double _igt[6] = {0};
|
|
int _d[2] = {1, -1};
|
|
int _dlast = 0;
|
|
int _dlastpos = 0;
|
|
double _w[2] = {0};
|
|
double _r[2] = {0};
|
|
double _xy[2] = {0};
|
|
int i;
|
|
int j;
|
|
int x;
|
|
int y;
|
|
|
|
LWGEOM *geom = NULL;
|
|
GEOSGeometry *sgeom = NULL;
|
|
GEOSGeometry *ngeom = NULL;
|
|
|
|
if (
|
|
(tolerance < 0.) ||
|
|
FLT_EQ(tolerance, 0.)
|
|
) {
|
|
tolerance = 0.1;
|
|
}
|
|
else if (tolerance > 1.)
|
|
tolerance = 1;
|
|
|
|
dbl_run = tolerance;
|
|
while (dbl_run < 10) {
|
|
dbl_run *= 10.;
|
|
max_run *= 10;
|
|
}
|
|
|
|
/* scale must be provided */
|
|
if (scale == NULL)
|
|
return NULL;
|
|
for (i = 0; i < 2; i++) {
|
|
if (FLT_EQ(scale[i], 0)) {
|
|
rterror("rt_raster_compute_skewed_raster: Scale cannot be zero");
|
|
return 0;
|
|
}
|
|
|
|
if (i < 1)
|
|
_gt[1] = fabs(scale[i] * tolerance);
|
|
else
|
|
_gt[5] = fabs(scale[i] * tolerance);
|
|
}
|
|
/* conform scale-y to be negative */
|
|
_gt[5] *= -1;
|
|
|
|
/* skew not provided or skew is zero, return raster of correct dim and spatial attributes */
|
|
if (
|
|
(skew == NULL) || (
|
|
FLT_EQ(skew[0], 0) &&
|
|
FLT_EQ(skew[1], 0)
|
|
)
|
|
) {
|
|
int _dim[2] = {
|
|
(int) fmax((fabs(extent.MaxX - extent.MinX) + (fabs(scale[0]) / 2.)) / fabs(scale[0]), 1),
|
|
(int) fmax((fabs(extent.MaxY - extent.MinY) + (fabs(scale[1]) / 2.)) / fabs(scale[1]), 1)
|
|
};
|
|
|
|
raster = rt_raster_new(_dim[0], _dim[1]);
|
|
if (raster == NULL) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not create output raster");
|
|
return NULL;
|
|
}
|
|
|
|
rt_raster_set_offsets(raster, extent.MinX, extent.MaxY);
|
|
rt_raster_set_scale(raster, fabs(scale[0]), -1 * fabs(scale[1]));
|
|
rt_raster_set_skews(raster, skew[0], skew[1]);
|
|
|
|
return raster;
|
|
}
|
|
|
|
/* direction to shift upper-left corner */
|
|
if (skew[0] > 0.)
|
|
_d[0] = -1;
|
|
if (skew[1] < 0.)
|
|
_d[1] = 1;
|
|
|
|
/* geotransform */
|
|
_gt[0] = extent.UpperLeftX;
|
|
_gt[2] = skew[0] * tolerance;
|
|
_gt[3] = extent.UpperLeftY;
|
|
_gt[4] = skew[1] * tolerance;
|
|
|
|
RASTER_DEBUGF(4, "Initial geotransform: %f, %f, %f, %f, %f, %f",
|
|
_gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]
|
|
);
|
|
RASTER_DEBUGF(4, "Delta: %d, %d", _d[0], _d[1]);
|
|
|
|
/* simple raster */
|
|
if ((raster = rt_raster_new(1, 1)) == NULL) {
|
|
rterror("rt_raster_compute_skewed_raster: Out of memory allocating extent raster");
|
|
return NULL;
|
|
}
|
|
rt_raster_set_geotransform_matrix(raster, _gt);
|
|
|
|
/* get inverse geotransform matrix */
|
|
if (!GDALInvGeoTransform(_gt, _igt)) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not compute inverse geotransform matrix");
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
RASTER_DEBUGF(4, "Inverse geotransform: %f, %f, %f, %f, %f, %f",
|
|
_igt[0], _igt[1], _igt[2], _igt[3], _igt[4], _igt[5]
|
|
);
|
|
|
|
/* shift along axis */
|
|
for (i = 0; i < 2; i++) {
|
|
covers = 0;
|
|
run = 0;
|
|
|
|
RASTER_DEBUGF(3, "Shifting along %s axis", i < 1 ? "X" : "Y");
|
|
|
|
do {
|
|
|
|
/* prevent possible infinite loop */
|
|
if (run > max_run) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not compute skewed extent due to check preventing infinite loop");
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
check the four corners that they are covered along the specific axis
|
|
pixel column should be >= 0
|
|
*/
|
|
for (j = 0; j < 4; j++) {
|
|
switch (j) {
|
|
/* upper-left */
|
|
case 0:
|
|
_xy[0] = extent.MinX;
|
|
_xy[1] = extent.MaxY;
|
|
break;
|
|
/* lower-left */
|
|
case 1:
|
|
_xy[0] = extent.MinX;
|
|
_xy[1] = extent.MinY;
|
|
break;
|
|
/* lower-right */
|
|
case 2:
|
|
_xy[0] = extent.MaxX;
|
|
_xy[1] = extent.MinY;
|
|
break;
|
|
/* upper-right */
|
|
case 3:
|
|
_xy[0] = extent.MaxX;
|
|
_xy[1] = extent.MaxY;
|
|
break;
|
|
}
|
|
|
|
rtn = rt_raster_geopoint_to_cell(
|
|
raster,
|
|
_xy[0], _xy[1],
|
|
&(_r[0]), &(_r[1]),
|
|
_igt
|
|
);
|
|
if (rtn != ES_NONE) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not compute raster pixel for spatial coordinates");
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
|
|
RASTER_DEBUGF(4, "Point %d at cell %d x %d", j, (int) _r[0], (int) _r[1]);
|
|
|
|
/* raster doesn't cover point */
|
|
if ((int) _r[i] < 0) {
|
|
RASTER_DEBUGF(4, "Point outside of skewed extent: %d", j);
|
|
covers = 0;
|
|
|
|
if (_dlastpos != j) {
|
|
_dlast = (int) _r[i];
|
|
_dlastpos = j;
|
|
}
|
|
else if ((int) _r[i] < _dlast) {
|
|
RASTER_DEBUG(4, "Point going in wrong direction. Reversing direction");
|
|
_d[i] *= -1;
|
|
_dlastpos = -1;
|
|
run = 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
covers++;
|
|
}
|
|
|
|
if (!covers) {
|
|
x = 0;
|
|
y = 0;
|
|
if (i < 1)
|
|
x = _d[i] * fabs(_r[i]);
|
|
else
|
|
y = _d[i] * fabs(_r[i]);
|
|
|
|
rtn = rt_raster_cell_to_geopoint(
|
|
raster,
|
|
x, y,
|
|
&(_w[0]), &(_w[1]),
|
|
_gt
|
|
);
|
|
if (rtn != ES_NONE) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not compute spatial coordinates for raster pixel");
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
|
|
/* adjust ul */
|
|
if (i < 1)
|
|
_gt[0] = _w[i];
|
|
else
|
|
_gt[3] = _w[i];
|
|
rt_raster_set_geotransform_matrix(raster, _gt);
|
|
RASTER_DEBUGF(4, "Shifted geotransform: %f, %f, %f, %f, %f, %f",
|
|
_gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]
|
|
);
|
|
|
|
/* get inverse geotransform matrix */
|
|
if (!GDALInvGeoTransform(_gt, _igt)) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not compute inverse geotransform matrix");
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
RASTER_DEBUGF(4, "Inverse geotransform: %f, %f, %f, %f, %f, %f",
|
|
_igt[0], _igt[1], _igt[2], _igt[3], _igt[4], _igt[5]
|
|
);
|
|
}
|
|
|
|
run++;
|
|
}
|
|
while (!covers);
|
|
}
|
|
|
|
/* covers test */
|
|
rtn = rt_raster_geopoint_to_cell(
|
|
raster,
|
|
extent.MaxX, extent.MinY,
|
|
&(_r[0]), &(_r[1]),
|
|
_igt
|
|
);
|
|
if (rtn != ES_NONE) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not compute raster pixel for spatial coordinates");
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
|
|
RASTER_DEBUGF(4, "geopoint %f x %f at cell %d x %d", extent.MaxX, extent.MinY, (int) _r[0], (int) _r[1]);
|
|
|
|
raster->width = _r[0];
|
|
raster->height = _r[1];
|
|
|
|
/* initialize GEOS */
|
|
initGEOS(rtinfo, lwgeom_geos_error);
|
|
|
|
/* create reference LWPOLY */
|
|
{
|
|
LWPOLY *npoly = rt_util_envelope_to_lwpoly(extent);
|
|
if (npoly == NULL) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not build extent's geometry for covers test");
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
|
|
ngeom = (GEOSGeometry *) LWGEOM2GEOS(lwpoly_as_lwgeom(npoly), 0);
|
|
lwpoly_free(npoly);
|
|
}
|
|
|
|
do {
|
|
covers = 0;
|
|
|
|
/* construct sgeom from raster */
|
|
if ((rt_raster_get_convex_hull(raster, &geom) != ES_NONE) || geom == NULL) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not build skewed extent's geometry for covers test");
|
|
GEOSGeom_destroy(ngeom);
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
|
|
sgeom = (GEOSGeometry *) LWGEOM2GEOS(geom, 0);
|
|
lwgeom_free(geom);
|
|
|
|
covers = GEOSRelatePattern(sgeom, ngeom, "******FF*");
|
|
GEOSGeom_destroy(sgeom);
|
|
|
|
if (covers == 2) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not run covers test");
|
|
GEOSGeom_destroy(ngeom);
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
|
|
if (covers)
|
|
break;
|
|
|
|
raster->width++;
|
|
raster->height++;
|
|
}
|
|
while (!covers);
|
|
|
|
RASTER_DEBUGF(4, "Skewed extent does cover normal extent with dimensions %d x %d", raster->width, raster->height);
|
|
|
|
raster->width = (int) ((((double) raster->width) * fabs(_gt[1]) + fabs(scale[0] / 2.)) / fabs(scale[0]));
|
|
raster->height = (int) ((((double) raster->height) * fabs(_gt[5]) + fabs(scale[1] / 2.)) / fabs(scale[1]));
|
|
_gt[1] = fabs(scale[0]);
|
|
_gt[5] = -1 * fabs(scale[1]);
|
|
_gt[2] = skew[0];
|
|
_gt[4] = skew[1];
|
|
rt_raster_set_geotransform_matrix(raster, _gt);
|
|
|
|
/* minimize width/height */
|
|
for (i = 0; i < 2; i++) {
|
|
covers = 1;
|
|
do {
|
|
if (i < 1)
|
|
raster->width--;
|
|
else
|
|
raster->height--;
|
|
|
|
/* construct sgeom from raster */
|
|
if ((rt_raster_get_convex_hull(raster, &geom) != ES_NONE) || geom == NULL) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not build skewed extent's geometry for minimizing dimensions");
|
|
GEOSGeom_destroy(ngeom);
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
|
|
sgeom = (GEOSGeometry *) LWGEOM2GEOS(geom, 0);
|
|
lwgeom_free(geom);
|
|
|
|
covers = GEOSRelatePattern(sgeom, ngeom, "******FF*");
|
|
GEOSGeom_destroy(sgeom);
|
|
|
|
if (covers == 2) {
|
|
rterror("rt_raster_compute_skewed_raster: Could not run covers test for minimizing dimensions");
|
|
GEOSGeom_destroy(ngeom);
|
|
rt_raster_destroy(raster);
|
|
return NULL;
|
|
}
|
|
|
|
if (!covers) {
|
|
if (i < 1)
|
|
raster->width++;
|
|
else
|
|
raster->height++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
while (covers);
|
|
}
|
|
|
|
GEOSGeom_destroy(ngeom);
|
|
|
|
return raster;
|
|
}
|
|
|
|
/**
|
|
* Return TRUE if the raster is empty. i.e. is NULL, width = 0 or height = 0
|
|
*
|
|
* @param raster : the raster to get info from
|
|
*
|
|
* @return TRUE if the raster is empty, FALSE otherwise
|
|
*/
|
|
int
|
|
rt_raster_is_empty(rt_raster raster) {
|
|
return (NULL == raster || raster->height <= 0 || raster->width <= 0);
|
|
}
|
|
|
|
/**
|
|
* Return TRUE if the raster has a band of this number.
|
|
*
|
|
* @param raster : the raster to get info from
|
|
* @param nband : the band number. 0-based
|
|
*
|
|
* @return TRUE if the raster has a band of this number, FALSE otherwise
|
|
*/
|
|
int
|
|
rt_raster_has_band(rt_raster raster, int nband) {
|
|
return !(NULL == raster || nband >= raster->numBands || nband < 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_copy_band()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Copy one band from one raster to another. Bands are duplicated from
|
|
* fromrast to torast using rt_band_duplicate. The caller will need
|
|
* to ensure that the copied band's data or path remains allocated
|
|
* for the lifetime of the copied bands.
|
|
*
|
|
* @param torast : raster to copy band to
|
|
* @param fromrast : raster to copy band from
|
|
* @param fromindex : index of band in source raster, 0-based
|
|
* @param toindex : index of new band in destination raster, 0-based
|
|
*
|
|
* @return The band index of the second raster where the new band is copied.
|
|
* -1 if error
|
|
*/
|
|
int
|
|
rt_raster_copy_band(
|
|
rt_raster torast, rt_raster fromrast,
|
|
int fromindex, int toindex
|
|
) {
|
|
rt_band srcband = NULL;
|
|
rt_band dstband = NULL;
|
|
|
|
assert(NULL != torast);
|
|
assert(NULL != fromrast);
|
|
|
|
/* Check raster dimensions */
|
|
if (torast->height != fromrast->height || torast->width != fromrast->width) {
|
|
rtwarn("rt_raster_copy_band: Attempting to add a band with different width or height");
|
|
return -1;
|
|
}
|
|
|
|
/* Check bands limits */
|
|
if (fromrast->numBands < 1) {
|
|
rtwarn("rt_raster_copy_band: Second raster has no band");
|
|
return -1;
|
|
}
|
|
else if (fromindex < 0) {
|
|
rtwarn("rt_raster_copy_band: Band index for second raster < 0. Defaulted to 0");
|
|
fromindex = 0;
|
|
}
|
|
else if (fromindex >= fromrast->numBands) {
|
|
rtwarn("rt_raster_copy_band: Band index for second raster > number of bands, truncated from %u to %u", fromindex, fromrast->numBands - 1);
|
|
fromindex = fromrast->numBands - 1;
|
|
}
|
|
|
|
if (toindex < 0) {
|
|
rtwarn("rt_raster_copy_band: Band index for first raster < 0. Defaulted to 0");
|
|
toindex = 0;
|
|
}
|
|
else if (toindex > torast->numBands) {
|
|
rtwarn("rt_raster_copy_band: Band index for first raster > number of bands, truncated from %u to %u", toindex, torast->numBands);
|
|
toindex = torast->numBands;
|
|
}
|
|
|
|
/* Get band from source raster */
|
|
srcband = rt_raster_get_band(fromrast, fromindex);
|
|
|
|
/* duplicate band */
|
|
dstband = rt_band_duplicate(srcband);
|
|
|
|
/* Add band to the second raster */
|
|
return rt_raster_add_band(torast, dstband, toindex);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_from_band()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Construct a new rt_raster from an existing rt_raster and an array
|
|
* of band numbers
|
|
*
|
|
* @param raster : the source raster
|
|
* @param bandNums : array of band numbers to extract from source raster
|
|
* and add to the new raster (0 based)
|
|
* @param count : number of elements in bandNums
|
|
*
|
|
* @return a new rt_raster or NULL on error
|
|
*/
|
|
rt_raster
|
|
rt_raster_from_band(rt_raster raster, uint32_t *bandNums, int count) {
|
|
rt_raster rast = NULL;
|
|
int i = 0;
|
|
int j = 0;
|
|
int idx;
|
|
int32_t flag;
|
|
double gt[6] = {0.};
|
|
|
|
assert(NULL != raster);
|
|
assert(NULL != bandNums);
|
|
|
|
RASTER_DEBUGF(3, "rt_raster_from_band: source raster has %d bands",
|
|
rt_raster_get_num_bands(raster));
|
|
|
|
/* create new raster */
|
|
rast = rt_raster_new(raster->width, raster->height);
|
|
if (NULL == rast) {
|
|
rterror("rt_raster_from_band: Out of memory allocating new raster");
|
|
return NULL;
|
|
}
|
|
|
|
/* copy raster attributes */
|
|
rt_raster_get_geotransform_matrix(raster, gt);
|
|
rt_raster_set_geotransform_matrix(rast, gt);
|
|
|
|
/* srid */
|
|
rt_raster_set_srid(rast, raster->srid);
|
|
|
|
/* copy bands */
|
|
for (i = 0; i < count; i++) {
|
|
idx = bandNums[i];
|
|
flag = rt_raster_copy_band(rast, raster, idx, i);
|
|
|
|
if (flag < 0) {
|
|
rterror("rt_raster_from_band: Could not copy band");
|
|
for (j = 0; j < i; j++) rt_band_destroy(rast->bands[j]);
|
|
rt_raster_destroy(rast);
|
|
return NULL;
|
|
}
|
|
|
|
RASTER_DEBUGF(3, "rt_raster_from_band: band created at index %d",
|
|
flag);
|
|
}
|
|
|
|
RASTER_DEBUGF(3, "rt_raster_from_band: new raster has %d bands",
|
|
rt_raster_get_num_bands(rast));
|
|
return rast;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_replace_band()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Replace band at provided index with new band
|
|
*
|
|
* @param raster: raster of band to be replaced
|
|
* @param band : new band to add to raster
|
|
* @param index : index of band to replace (0-based)
|
|
*
|
|
* @return NULL on error or replaced band
|
|
*/
|
|
rt_band
|
|
rt_raster_replace_band(rt_raster raster, rt_band band, int index) {
|
|
rt_band oldband = NULL;
|
|
assert(NULL != raster);
|
|
assert(NULL != band);
|
|
|
|
if (band->width != raster->width || band->height != raster->height) {
|
|
rterror("rt_raster_replace_band: Band does not match raster's dimensions: %dx%d band to %dx%d raster",
|
|
band->width, band->height, raster->width, raster->height);
|
|
return 0;
|
|
}
|
|
|
|
if (index >= raster->numBands || index < 0) {
|
|
rterror("rt_raster_replace_band: Band index is not valid");
|
|
return 0;
|
|
}
|
|
|
|
oldband = rt_raster_get_band(raster, index);
|
|
RASTER_DEBUGF(3, "rt_raster_replace_band: old band at %p", oldband);
|
|
RASTER_DEBUGF(3, "rt_raster_replace_band: new band at %p", band);
|
|
|
|
raster->bands[index] = band;
|
|
RASTER_DEBUGF(3, "rt_raster_replace_band: new band at %p", raster->bands[index]);
|
|
|
|
band->raster = raster;
|
|
oldband->raster = NULL;
|
|
|
|
return oldband;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_clone()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Clone an existing raster
|
|
*
|
|
* @param raster : raster to clone
|
|
* @param deep : flag indicating if bands should be cloned
|
|
*
|
|
* @return a new rt_raster or NULL on error
|
|
*/
|
|
rt_raster
|
|
rt_raster_clone(rt_raster raster, uint8_t deep) {
|
|
rt_raster rtn = NULL;
|
|
double gt[6] = {0};
|
|
|
|
assert(NULL != raster);
|
|
|
|
if (deep) {
|
|
int numband = rt_raster_get_num_bands(raster);
|
|
uint32_t *nband = NULL;
|
|
int i = 0;
|
|
|
|
nband = rtalloc(sizeof(uint32_t) * numband);
|
|
if (nband == NULL) {
|
|
rterror("rt_raster_clone: Could not allocate memory for deep clone");
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < numband; i++)
|
|
nband[i] = i;
|
|
|
|
rtn = rt_raster_from_band(raster, nband, numband);
|
|
rtdealloc(nband);
|
|
|
|
return rtn;
|
|
}
|
|
|
|
rtn = rt_raster_new(
|
|
rt_raster_get_width(raster),
|
|
rt_raster_get_height(raster)
|
|
);
|
|
if (rtn == NULL) {
|
|
rterror("rt_raster_clone: Could not create cloned raster");
|
|
return NULL;
|
|
}
|
|
|
|
rt_raster_get_geotransform_matrix(raster, gt);
|
|
rt_raster_set_geotransform_matrix(rtn, gt);
|
|
rt_raster_set_srid(rtn, rt_raster_get_srid(raster));
|
|
|
|
return rtn;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_to_gdal()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Return formatted GDAL raster from raster
|
|
*
|
|
* @param raster : the raster to convert
|
|
* @param srs : the raster's coordinate system in OGC WKT
|
|
* @param format : format to convert to. GDAL driver short name
|
|
* @param options : list of format creation options. array of strings
|
|
* @param gdalsize : will be set to the size of returned bytea
|
|
*
|
|
* @return formatted GDAL raster. the calling function is responsible
|
|
* for freeing the returned data using CPLFree()
|
|
*/
|
|
uint8_t*
|
|
rt_raster_to_gdal(
|
|
rt_raster raster, const char *srs,
|
|
char *format, char **options, uint64_t *gdalsize
|
|
) {
|
|
GDALDriverH src_drv = NULL;
|
|
int destroy_src_drv = 0;
|
|
GDALDatasetH src_ds = NULL;
|
|
|
|
vsi_l_offset rtn_lenvsi;
|
|
uint64_t rtn_len = 0;
|
|
|
|
GDALDriverH rtn_drv = NULL;
|
|
GDALDatasetH rtn_ds = NULL;
|
|
uint8_t *rtn = NULL;
|
|
|
|
assert(NULL != raster);
|
|
assert(NULL != gdalsize);
|
|
|
|
/* any supported format is possible */
|
|
rt_util_gdal_register_all(0);
|
|
RASTER_DEBUG(3, "loaded all supported GDAL formats");
|
|
|
|
/* output format not specified */
|
|
if (format == NULL || !strlen(format))
|
|
format = "GTiff";
|
|
RASTER_DEBUGF(3, "output format is %s", format);
|
|
|
|
/* load raster into a GDAL MEM raster */
|
|
src_ds = rt_raster_to_gdal_mem(raster, srs, NULL, NULL, 0, &src_drv, &destroy_src_drv);
|
|
if (NULL == src_ds) {
|
|
rterror("rt_raster_to_gdal: Could not convert raster to GDAL MEM format");
|
|
return 0;
|
|
}
|
|
|
|
/* load driver */
|
|
rtn_drv = GDALGetDriverByName(format);
|
|
if (NULL == rtn_drv) {
|
|
rterror("rt_raster_to_gdal: Could not load the output GDAL driver");
|
|
GDALClose(src_ds);
|
|
if (destroy_src_drv) GDALDestroyDriver(src_drv);
|
|
return 0;
|
|
}
|
|
RASTER_DEBUG(3, "Output driver loaded");
|
|
|
|
/* convert GDAL MEM raster to output format */
|
|
RASTER_DEBUG(3, "Copying GDAL MEM raster to memory file in output format");
|
|
rtn_ds = GDALCreateCopy(
|
|
rtn_drv,
|
|
"/vsimem/out.dat", /* should be fine assuming this is in a process */
|
|
src_ds,
|
|
FALSE, /* should copy be strictly equivelent? */
|
|
options, /* format options */
|
|
NULL, /* progress function */
|
|
NULL /* progress data */
|
|
);
|
|
|
|
/* close source dataset */
|
|
GDALClose(src_ds);
|
|
if (destroy_src_drv) GDALDestroyDriver(src_drv);
|
|
RASTER_DEBUG(3, "Closed GDAL MEM raster");
|
|
|
|
if (NULL == rtn_ds) {
|
|
rterror("rt_raster_to_gdal: Could not create the output GDAL dataset");
|
|
return 0;
|
|
}
|
|
|
|
RASTER_DEBUGF(4, "dataset SRS: %s", GDALGetProjectionRef(rtn_ds));
|
|
|
|
/* close dataset, this also flushes any pending writes */
|
|
GDALClose(rtn_ds);
|
|
RASTER_DEBUG(3, "Closed GDAL output raster");
|
|
|
|
RASTER_DEBUG(3, "Done copying GDAL MEM raster to memory file in output format");
|
|
|
|
/* from memory file to buffer */
|
|
RASTER_DEBUG(3, "Copying GDAL memory file to buffer");
|
|
rtn = VSIGetMemFileBuffer("/vsimem/out.dat", &rtn_lenvsi, TRUE);
|
|
RASTER_DEBUG(3, "Done copying GDAL memory file to buffer");
|
|
if (NULL == rtn) {
|
|
rterror("rt_raster_to_gdal: Could not create the output GDAL raster");
|
|
return 0;
|
|
}
|
|
|
|
rtn_len = (uint64_t) rtn_lenvsi;
|
|
*gdalsize = rtn_len;
|
|
|
|
return rtn;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_gdal_drivers()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Returns a set of available GDAL drivers
|
|
*
|
|
* @param drv_count : number of GDAL drivers available
|
|
* @param cancc : if non-zero, filter drivers to only those
|
|
* with support for CreateCopy and VirtualIO
|
|
*
|
|
* @return set of "gdaldriver" values of available GDAL drivers
|
|
*/
|
|
rt_gdaldriver
|
|
rt_raster_gdal_drivers(uint32_t *drv_count, uint8_t cancc) {
|
|
const char *state;
|
|
const char *txt;
|
|
int txt_len;
|
|
GDALDriverH *drv = NULL;
|
|
rt_gdaldriver rtn = NULL;
|
|
int count;
|
|
int i;
|
|
uint32_t j;
|
|
|
|
assert(drv_count != NULL);
|
|
|
|
rt_util_gdal_register_all(0);
|
|
count = GDALGetDriverCount();
|
|
RASTER_DEBUGF(3, "%d drivers found", count);
|
|
|
|
rtn = (rt_gdaldriver) rtalloc(count * sizeof(struct rt_gdaldriver_t));
|
|
if (NULL == rtn) {
|
|
rterror("rt_raster_gdal_drivers: Could not allocate memory for gdaldriver structure");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0, j = 0; i < count; i++) {
|
|
drv = GDALGetDriver(i);
|
|
|
|
#ifdef GDAL_DCAP_RASTER
|
|
/* Starting with GDAL 2.0, vector drivers can also be returned */
|
|
/* Only keep raster drivers */
|
|
state = GDALGetMetadataItem(drv, GDAL_DCAP_RASTER, NULL);
|
|
if (state == NULL || !EQUAL(state, "YES"))
|
|
continue;
|
|
#endif
|
|
|
|
if (cancc) {
|
|
/* CreateCopy support */
|
|
state = GDALGetMetadataItem(drv, GDAL_DCAP_CREATECOPY, NULL);
|
|
if (state == NULL) continue;
|
|
|
|
/* VirtualIO support */
|
|
state = GDALGetMetadataItem(drv, GDAL_DCAP_VIRTUALIO, NULL);
|
|
if (state == NULL) continue;
|
|
}
|
|
|
|
/* index of driver */
|
|
rtn[j].idx = i;
|
|
|
|
/* short name */
|
|
txt = GDALGetDriverShortName(drv);
|
|
txt_len = strlen(txt);
|
|
|
|
if (cancc) {
|
|
RASTER_DEBUGF(3, "driver %s (%d) supports CreateCopy() and VirtualIO()", txt, i);
|
|
}
|
|
|
|
txt_len = (txt_len + 1) * sizeof(char);
|
|
rtn[j].short_name = (char *) rtalloc(txt_len);
|
|
memcpy(rtn[j].short_name, txt, txt_len);
|
|
|
|
/* long name */
|
|
txt = GDALGetDriverLongName(drv);
|
|
txt_len = strlen(txt);
|
|
|
|
txt_len = (txt_len + 1) * sizeof(char);
|
|
rtn[j].long_name = (char *) rtalloc(txt_len);
|
|
memcpy(rtn[j].long_name, txt, txt_len);
|
|
|
|
/* creation options */
|
|
txt = GDALGetDriverCreationOptionList(drv);
|
|
txt_len = strlen(txt);
|
|
|
|
txt_len = (txt_len + 1) * sizeof(char);
|
|
rtn[j].create_options = (char *) rtalloc(txt_len);
|
|
memcpy(rtn[j].create_options, txt, txt_len);
|
|
|
|
j++;
|
|
}
|
|
|
|
/* free unused memory */
|
|
rtn = rtrealloc(rtn, j * sizeof(struct rt_gdaldriver_t));
|
|
*drv_count = j;
|
|
|
|
return rtn;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_to_gdal_mem()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Return GDAL dataset using GDAL MEM driver from raster.
|
|
*
|
|
* @param raster : raster to convert to GDAL MEM
|
|
* @param srs : the raster's coordinate system in OGC WKT
|
|
* @param bandNums : array of band numbers to extract from raster
|
|
* and include in the GDAL dataset (0 based)
|
|
* @param excludeNodataValues : array of zero, nonzero where if non-zero,
|
|
* ignore nodata values for the band
|
|
* @param count : number of elements in bandNums
|
|
* @param rtn_drv : is set to the GDAL driver object
|
|
* @param destroy_rtn_drv : if non-zero, caller must destroy the MEM driver
|
|
*
|
|
* @return GDAL dataset using GDAL MEM driver
|
|
*/
|
|
GDALDatasetH
|
|
rt_raster_to_gdal_mem(
|
|
rt_raster raster,
|
|
const char *srs,
|
|
uint32_t *bandNums,
|
|
int *excludeNodataValues,
|
|
int count,
|
|
GDALDriverH *rtn_drv, int *destroy_rtn_drv
|
|
) {
|
|
GDALDriverH drv = NULL;
|
|
GDALDatasetH ds = NULL;
|
|
double gt[6] = {0.0};
|
|
CPLErr cplerr;
|
|
GDALDataType gdal_pt = GDT_Unknown;
|
|
GDALRasterBandH band;
|
|
void *pVoid;
|
|
char *pszDataPointer;
|
|
char szGDALOption[50];
|
|
char *apszOptions[4];
|
|
double nodata = 0.0;
|
|
int allocBandNums = 0;
|
|
int allocNodataValues = 0;
|
|
|
|
int i;
|
|
int numBands;
|
|
uint32_t width = 0;
|
|
uint32_t height = 0;
|
|
rt_band rtband = NULL;
|
|
rt_pixtype pt = PT_END;
|
|
|
|
assert(NULL != raster);
|
|
assert(NULL != rtn_drv);
|
|
assert(NULL != destroy_rtn_drv);
|
|
|
|
*destroy_rtn_drv = 0;
|
|
|
|
/* store raster in GDAL MEM raster */
|
|
if (!rt_util_gdal_driver_registered("MEM")) {
|
|
RASTER_DEBUG(4, "Registering MEM driver");
|
|
GDALRegister_MEM();
|
|
*destroy_rtn_drv = 1;
|
|
}
|
|
drv = GDALGetDriverByName("MEM");
|
|
if (NULL == drv) {
|
|
rterror("rt_raster_to_gdal_mem: Could not load the MEM GDAL driver");
|
|
return 0;
|
|
}
|
|
*rtn_drv = drv;
|
|
|
|
/* unload driver from GDAL driver manager */
|
|
if (*destroy_rtn_drv) {
|
|
RASTER_DEBUG(4, "Deregistering MEM driver");
|
|
GDALDeregisterDriver(drv);
|
|
}
|
|
|
|
width = rt_raster_get_width(raster);
|
|
height = rt_raster_get_height(raster);
|
|
ds = GDALCreate(
|
|
drv, "",
|
|
width, height,
|
|
0, GDT_Byte, NULL
|
|
);
|
|
if (NULL == ds) {
|
|
rterror("rt_raster_to_gdal_mem: Could not create a GDALDataset to convert into");
|
|
return 0;
|
|
}
|
|
|
|
/* add geotransform */
|
|
rt_raster_get_geotransform_matrix(raster, gt);
|
|
cplerr = GDALSetGeoTransform(ds, gt);
|
|
if (cplerr != CE_None) {
|
|
rterror("rt_raster_to_gdal_mem: Could not set geotransformation");
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
|
|
/* set spatial reference */
|
|
if (NULL != srs && strlen(srs)) {
|
|
char *_srs = rt_util_gdal_convert_sr(srs, 0);
|
|
if (_srs == NULL) {
|
|
rterror("rt_raster_to_gdal_mem: Could not convert srs to GDAL accepted format");
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
|
|
cplerr = GDALSetProjection(ds, _srs);
|
|
CPLFree(_srs);
|
|
if (cplerr != CE_None) {
|
|
rterror("rt_raster_to_gdal_mem: Could not set projection");
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
RASTER_DEBUGF(3, "Projection set to: %s", GDALGetProjectionRef(ds));
|
|
}
|
|
|
|
/* process bandNums */
|
|
numBands = rt_raster_get_num_bands(raster);
|
|
if (NULL != bandNums && count > 0) {
|
|
for (i = 0; i < count; i++) {
|
|
if (bandNums[i] >= numBands) {
|
|
rterror("rt_raster_to_gdal_mem: The band index %d is invalid", bandNums[i]);
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
count = numBands;
|
|
bandNums = (uint32_t *) rtalloc(sizeof(uint32_t) * count);
|
|
if (NULL == bandNums) {
|
|
rterror("rt_raster_to_gdal_mem: Could not allocate memory for band indices");
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
allocBandNums = 1;
|
|
for (i = 0; i < count; i++) bandNums[i] = i;
|
|
}
|
|
|
|
/* process exclude_nodata_values */
|
|
if (NULL == excludeNodataValues) {
|
|
excludeNodataValues = (int *) rtalloc(sizeof(int) * count);
|
|
if (NULL == excludeNodataValues) {
|
|
rterror("rt_raster_to_gdal_mem: Could not allocate memory for NODATA flags");
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
allocNodataValues = 1;
|
|
for (i = 0; i < count; i++) excludeNodataValues[i] = 1;
|
|
}
|
|
|
|
/* add band(s) */
|
|
for (i = 0; i < count; i++) {
|
|
rtband = rt_raster_get_band(raster, bandNums[i]);
|
|
if (NULL == rtband) {
|
|
rterror("rt_raster_to_gdal_mem: Could not get requested band index %d", bandNums[i]);
|
|
if (allocBandNums) rtdealloc(bandNums);
|
|
if (allocNodataValues) rtdealloc(excludeNodataValues);
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
|
|
pt = rt_band_get_pixtype(rtband);
|
|
gdal_pt = rt_util_pixtype_to_gdal_datatype(pt);
|
|
if (gdal_pt == GDT_Unknown)
|
|
rtwarn("rt_raster_to_gdal_mem: Unknown pixel type for band");
|
|
|
|
/*
|
|
For all pixel types other than PT_8BSI, set pointer to start of data
|
|
*/
|
|
if (pt != PT_8BSI) {
|
|
pVoid = rt_band_get_data(rtband);
|
|
RASTER_DEBUGF(4, "Band data is at pos %p", pVoid);
|
|
|
|
pszDataPointer = (char *) rtalloc(20 * sizeof (char));
|
|
sprintf(pszDataPointer, "%p", pVoid);
|
|
RASTER_DEBUGF(4, "rt_raster_to_gdal_mem: szDatapointer is %p",
|
|
pszDataPointer);
|
|
|
|
if (strnicmp(pszDataPointer, "0x", 2) == 0)
|
|
sprintf(szGDALOption, "DATAPOINTER=%s", pszDataPointer);
|
|
else
|
|
sprintf(szGDALOption, "DATAPOINTER=0x%s", pszDataPointer);
|
|
|
|
RASTER_DEBUG(3, "Storing info for GDAL MEM raster band");
|
|
|
|
apszOptions[0] = szGDALOption;
|
|
apszOptions[1] = NULL; /* pixel offset, not needed */
|
|
apszOptions[2] = NULL; /* line offset, not needed */
|
|
apszOptions[3] = NULL;
|
|
|
|
/* free */
|
|
rtdealloc(pszDataPointer);
|
|
|
|
/* add band */
|
|
if (GDALAddBand(ds, gdal_pt, apszOptions) == CE_Failure) {
|
|
rterror("rt_raster_to_gdal_mem: Could not add GDAL raster band");
|
|
if (allocBandNums) rtdealloc(bandNums);
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
}
|
|
/*
|
|
PT_8BSI is special as GDAL has no equivalent pixel type.
|
|
Must convert 8BSI to 16BSI so create basic band
|
|
*/
|
|
else {
|
|
/* add band */
|
|
if (GDALAddBand(ds, gdal_pt, NULL) == CE_Failure) {
|
|
rterror("rt_raster_to_gdal_mem: Could not add GDAL raster band");
|
|
if (allocBandNums) rtdealloc(bandNums);
|
|
if (allocNodataValues) rtdealloc(excludeNodataValues);
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* check band count */
|
|
if (GDALGetRasterCount(ds) != i + 1) {
|
|
rterror("rt_raster_to_gdal_mem: Error creating GDAL MEM raster band");
|
|
if (allocBandNums) rtdealloc(bandNums);
|
|
if (allocNodataValues) rtdealloc(excludeNodataValues);
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
|
|
/* get new band */
|
|
band = NULL;
|
|
band = GDALGetRasterBand(ds, i + 1);
|
|
if (NULL == band) {
|
|
rterror("rt_raster_to_gdal_mem: Could not get GDAL band for additional processing");
|
|
if (allocBandNums) rtdealloc(bandNums);
|
|
if (allocNodataValues) rtdealloc(excludeNodataValues);
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
|
|
/* PT_8BSI requires manual setting of pixels */
|
|
if (pt == PT_8BSI) {
|
|
int nXBlocks, nYBlocks;
|
|
int nXBlockSize, nYBlockSize;
|
|
int iXBlock, iYBlock;
|
|
int nXValid, nYValid;
|
|
int iX, iY;
|
|
int iXMax, iYMax;
|
|
|
|
int x, y, z;
|
|
uint32_t valueslen = 0;
|
|
int16_t *values = NULL;
|
|
double value = 0.;
|
|
|
|
/* this makes use of GDAL's "natural" blocks */
|
|
GDALGetBlockSize(band, &nXBlockSize, &nYBlockSize);
|
|
nXBlocks = (width + nXBlockSize - 1) / nXBlockSize;
|
|
nYBlocks = (height + nYBlockSize - 1) / nYBlockSize;
|
|
RASTER_DEBUGF(4, "(nXBlockSize, nYBlockSize) = (%d, %d)", nXBlockSize, nYBlockSize);
|
|
RASTER_DEBUGF(4, "(nXBlocks, nYBlocks) = (%d, %d)", nXBlocks, nYBlocks);
|
|
|
|
/* length is for the desired pixel type */
|
|
valueslen = rt_pixtype_size(PT_16BSI) * nXBlockSize * nYBlockSize;
|
|
values = rtalloc(valueslen);
|
|
if (NULL == values) {
|
|
rterror("rt_raster_to_gdal_mem: Could not allocate memory for GDAL band pixel values");
|
|
if (allocBandNums) rtdealloc(bandNums);
|
|
if (allocNodataValues) rtdealloc(excludeNodataValues);
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
|
|
for (iYBlock = 0; iYBlock < nYBlocks; iYBlock++) {
|
|
for (iXBlock = 0; iXBlock < nXBlocks; iXBlock++) {
|
|
memset(values, 0, valueslen);
|
|
|
|
x = iXBlock * nXBlockSize;
|
|
y = iYBlock * nYBlockSize;
|
|
RASTER_DEBUGF(4, "(iXBlock, iYBlock) = (%d, %d)", iXBlock, iYBlock);
|
|
RASTER_DEBUGF(4, "(x, y) = (%d, %d)", x, y);
|
|
|
|
/* valid block width */
|
|
if ((iXBlock + 1) * nXBlockSize > width)
|
|
nXValid = width - (iXBlock * nXBlockSize);
|
|
else
|
|
nXValid = nXBlockSize;
|
|
|
|
/* valid block height */
|
|
if ((iYBlock + 1) * nYBlockSize > height)
|
|
nYValid = height - (iYBlock * nYBlockSize);
|
|
else
|
|
nYValid = nYBlockSize;
|
|
|
|
RASTER_DEBUGF(4, "(nXValid, nYValid) = (%d, %d)", nXValid, nYValid);
|
|
|
|
/* convert 8BSI values to 16BSI */
|
|
z = 0;
|
|
iYMax = y + nYValid;
|
|
iXMax = x + nXValid;
|
|
for (iY = y; iY < iYMax; iY++) {
|
|
for (iX = x; iX < iXMax; iX++) {
|
|
if (rt_band_get_pixel(rtband, iX, iY, &value, NULL) != ES_NONE) {
|
|
rterror("rt_raster_to_gdal_mem: Could not get pixel value to convert from 8BSI to 16BSI");
|
|
rtdealloc(values);
|
|
if (allocBandNums) rtdealloc(bandNums);
|
|
if (allocNodataValues) rtdealloc(excludeNodataValues);
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
|
|
values[z++] = rt_util_clamp_to_16BSI(value);
|
|
}
|
|
}
|
|
|
|
/* burn values */
|
|
if (GDALRasterIO(
|
|
band, GF_Write,
|
|
x, y,
|
|
nXValid, nYValid,
|
|
values, nXValid, nYValid,
|
|
gdal_pt,
|
|
0, 0
|
|
) != CE_None) {
|
|
rterror("rt_raster_to_gdal_mem: Could not write converted 8BSI to 16BSI values to GDAL band");
|
|
rtdealloc(values);
|
|
if (allocBandNums) rtdealloc(bandNums);
|
|
if (allocNodataValues) rtdealloc(excludeNodataValues);
|
|
GDALClose(ds);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
rtdealloc(values);
|
|
}
|
|
|
|
/* Add nodata value for band */
|
|
if (rt_band_get_hasnodata_flag(rtband) != FALSE && excludeNodataValues[i]) {
|
|
rt_band_get_nodata(rtband, &nodata);
|
|
if (GDALSetRasterNoDataValue(band, nodata) != CE_None)
|
|
rtwarn("rt_raster_to_gdal_mem: Could not set nodata value for band");
|
|
RASTER_DEBUGF(3, "nodata value set to %f", GDALGetRasterNoDataValue(band, NULL));
|
|
}
|
|
|
|
#if POSTGIS_DEBUG_LEVEL > 3
|
|
{
|
|
GDALRasterBandH _grb = NULL;
|
|
double _min;
|
|
double _max;
|
|
double _mean;
|
|
double _stddev;
|
|
|
|
_grb = GDALGetRasterBand(ds, i + 1);
|
|
GDALComputeRasterStatistics(_grb, FALSE, &_min, &_max, &_mean, &_stddev, NULL, NULL);
|
|
RASTER_DEBUGF(4, "GDAL Band %d stats: %f, %f, %f, %f", i + 1, _min, _max, _mean, _stddev);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
/* necessary??? */
|
|
GDALFlushCache(ds);
|
|
|
|
if (allocBandNums) rtdealloc(bandNums);
|
|
if (allocNodataValues) rtdealloc(excludeNodataValues);
|
|
|
|
return ds;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_from_gdal_dataset()
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Return a raster from a GDAL dataset
|
|
*
|
|
* @param ds : the GDAL dataset to convert to a raster
|
|
*
|
|
* @return raster or NULL
|
|
*/
|
|
rt_raster
|
|
rt_raster_from_gdal_dataset(GDALDatasetH ds) {
|
|
rt_raster rast = NULL;
|
|
double gt[6] = {0};
|
|
CPLErr cplerr;
|
|
uint32_t width = 0;
|
|
uint32_t height = 0;
|
|
uint32_t numBands = 0;
|
|
int i = 0;
|
|
char *authname = NULL;
|
|
char *authcode = NULL;
|
|
|
|
GDALRasterBandH gdband = NULL;
|
|
GDALDataType gdpixtype = GDT_Unknown;
|
|
rt_band band;
|
|
int32_t idx;
|
|
rt_pixtype pt = PT_END;
|
|
uint32_t ptlen = 0;
|
|
int hasnodata = 0;
|
|
double nodataval;
|
|
|
|
int x;
|
|
int y;
|
|
|
|
int nXBlocks, nYBlocks;
|
|
int nXBlockSize, nYBlockSize;
|
|
int iXBlock, iYBlock;
|
|
int nXValid, nYValid;
|
|
int iY;
|
|
|
|
uint8_t *values = NULL;
|
|
uint32_t valueslen = 0;
|
|
uint8_t *ptr = NULL;
|
|
|
|
assert(NULL != ds);
|
|
|
|
/* raster size */
|
|
width = GDALGetRasterXSize(ds);
|
|
height = GDALGetRasterYSize(ds);
|
|
RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d", width, height);
|
|
|
|
/* create new raster */
|
|
RASTER_DEBUG(3, "Creating new raster");
|
|
rast = rt_raster_new(width, height);
|
|
if (NULL == rast) {
|
|
rterror("rt_raster_from_gdal_dataset: Out of memory allocating new raster");
|
|
return NULL;
|
|
}
|
|
RASTER_DEBUGF(3, "Created raster dimensions (width x height): %d x %d", rast->width, rast->height);
|
|
|
|
/* get raster attributes */
|
|
cplerr = GDALGetGeoTransform(ds, gt);
|
|
if (GDALGetGeoTransform(ds, gt) != CE_None) {
|
|
RASTER_DEBUG(4, "Using default geotransform matrix (0, 1, 0, 0, 0, -1)");
|
|
gt[0] = 0;
|
|
gt[1] = 1;
|
|
gt[2] = 0;
|
|
gt[3] = 0;
|
|
gt[4] = 0;
|
|
gt[5] = -1;
|
|
}
|
|
|
|
/* apply raster attributes */
|
|
rt_raster_set_geotransform_matrix(rast, gt);
|
|
|
|
RASTER_DEBUGF(3, "Raster geotransform (%f, %f, %f, %f, %f, %f)",
|
|
gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
|
|
|
|
/* srid */
|
|
if (rt_util_gdal_sr_auth_info(ds, &authname, &authcode) == ES_NONE) {
|
|
if (
|
|
authname != NULL &&
|
|
strcmp(authname, "EPSG") == 0 &&
|
|
authcode != NULL
|
|
) {
|
|
rt_raster_set_srid(rast, atoi(authcode));
|
|
RASTER_DEBUGF(3, "New raster's SRID = %d", rast->srid);
|
|
}
|
|
|
|
if (authname != NULL)
|
|
rtdealloc(authname);
|
|
if (authcode != NULL)
|
|
rtdealloc(authcode);
|
|
}
|
|
|
|
numBands = GDALGetRasterCount(ds);
|
|
|
|
#if POSTGIS_DEBUG_LEVEL > 3
|
|
for (i = 1; i <= numBands; i++) {
|
|
GDALRasterBandH _grb = NULL;
|
|
double _min;
|
|
double _max;
|
|
double _mean;
|
|
double _stddev;
|
|
|
|
_grb = GDALGetRasterBand(ds, i);
|
|
GDALComputeRasterStatistics(_grb, FALSE, &_min, &_max, &_mean, &_stddev, NULL, NULL);
|
|
RASTER_DEBUGF(4, "GDAL Band %d stats: %f, %f, %f, %f", i, _min, _max, _mean, _stddev);
|
|
}
|
|
#endif
|
|
|
|
/* copy bands */
|
|
for (i = 1; i <= numBands; i++) {
|
|
RASTER_DEBUGF(3, "Processing band %d of %d", i, numBands);
|
|
gdband = NULL;
|
|
gdband = GDALGetRasterBand(ds, i);
|
|
if (NULL == gdband) {
|
|
rterror("rt_raster_from_gdal_dataset: Could not get GDAL band");
|
|
rt_raster_destroy(rast);
|
|
return NULL;
|
|
}
|
|
RASTER_DEBUGF(4, "gdband @ %p", gdband);
|
|
|
|
/* pixtype */
|
|
gdpixtype = GDALGetRasterDataType(gdband);
|
|
RASTER_DEBUGF(4, "gdpixtype, size = %s, %d", GDALGetDataTypeName(gdpixtype), GDALGetDataTypeSize(gdpixtype) / 8);
|
|
pt = rt_util_gdal_datatype_to_pixtype(gdpixtype);
|
|
if (pt == PT_END) {
|
|
rterror("rt_raster_from_gdal_dataset: Unknown pixel type for GDAL band");
|
|
rt_raster_destroy(rast);
|
|
return NULL;
|
|
}
|
|
ptlen = rt_pixtype_size(pt);
|
|
|
|
/* size: width and height */
|
|
width = GDALGetRasterBandXSize(gdband);
|
|
height = GDALGetRasterBandYSize(gdband);
|
|
RASTER_DEBUGF(3, "GDAL band dimensions (width x height): %d x %d", width, height);
|
|
|
|
/* nodata */
|
|
nodataval = GDALGetRasterNoDataValue(gdband, &hasnodata);
|
|
RASTER_DEBUGF(3, "(hasnodata, nodataval) = (%d, %f)", hasnodata, nodataval);
|
|
|
|
/* create band object */
|
|
idx = rt_raster_generate_new_band(
|
|
rast, pt,
|
|
(hasnodata ? nodataval : 0),
|
|
hasnodata, nodataval, rt_raster_get_num_bands(rast)
|
|
);
|
|
if (idx < 0) {
|
|
rterror("rt_raster_from_gdal_dataset: Could not allocate memory for raster band");
|
|
rt_raster_destroy(rast);
|
|
return NULL;
|
|
}
|
|
band = rt_raster_get_band(rast, idx);
|
|
RASTER_DEBUGF(3, "Created band of dimension (width x height): %d x %d", band->width, band->height);
|
|
|
|
/* this makes use of GDAL's "natural" blocks */
|
|
GDALGetBlockSize(gdband, &nXBlockSize, &nYBlockSize);
|
|
nXBlocks = (width + nXBlockSize - 1) / nXBlockSize;
|
|
nYBlocks = (height + nYBlockSize - 1) / nYBlockSize;
|
|
RASTER_DEBUGF(4, "(nXBlockSize, nYBlockSize) = (%d, %d)", nXBlockSize, nYBlockSize);
|
|
RASTER_DEBUGF(4, "(nXBlocks, nYBlocks) = (%d, %d)", nXBlocks, nYBlocks);
|
|
|
|
/* allocate memory for values */
|
|
valueslen = ptlen * nXBlockSize * nYBlockSize;
|
|
values = rtalloc(valueslen);
|
|
if (values == NULL) {
|
|
rterror("rt_raster_from_gdal_dataset: Could not allocate memory for GDAL band pixel values");
|
|
rt_raster_destroy(rast);
|
|
return NULL;
|
|
}
|
|
RASTER_DEBUGF(3, "values @ %p of length = %d", values, valueslen);
|
|
|
|
for (iYBlock = 0; iYBlock < nYBlocks; iYBlock++) {
|
|
for (iXBlock = 0; iXBlock < nXBlocks; iXBlock++) {
|
|
x = iXBlock * nXBlockSize;
|
|
y = iYBlock * nYBlockSize;
|
|
RASTER_DEBUGF(4, "(iXBlock, iYBlock) = (%d, %d)", iXBlock, iYBlock);
|
|
RASTER_DEBUGF(4, "(x, y) = (%d, %d)", x, y);
|
|
|
|
memset(values, 0, valueslen);
|
|
|
|
/* valid block width */
|
|
if ((iXBlock + 1) * nXBlockSize > width)
|
|
nXValid = width - (iXBlock * nXBlockSize);
|
|
else
|
|
nXValid = nXBlockSize;
|
|
|
|
/* valid block height */
|
|
if ((iYBlock + 1) * nYBlockSize > height)
|
|
nYValid = height - (iYBlock * nYBlockSize);
|
|
else
|
|
nYValid = nYBlockSize;
|
|
|
|
RASTER_DEBUGF(4, "(nXValid, nYValid) = (%d, %d)", nXValid, nYValid);
|
|
|
|
cplerr = GDALRasterIO(
|
|
gdband, GF_Read,
|
|
x, y,
|
|
nXValid, nYValid,
|
|
values, nXValid, nYValid,
|
|
gdpixtype,
|
|
0, 0
|
|
);
|
|
if (cplerr != CE_None) {
|
|
rterror("rt_raster_from_gdal_dataset: Could not get data from GDAL raster");
|
|
rtdealloc(values);
|
|
rt_raster_destroy(rast);
|
|
return NULL;
|
|
}
|
|
|
|
/* if block width is same as raster width, shortcut */
|
|
if (nXBlocks == 1 && nYBlockSize > 1 && nXValid == width) {
|
|
x = 0;
|
|
y = nYBlockSize * iYBlock;
|
|
|
|
RASTER_DEBUGF(4, "Setting set of pixel lines at (%d, %d) for %d pixels", x, y, nXValid * nYValid);
|
|
rt_band_set_pixel_line(band, x, y, values, nXValid * nYValid);
|
|
}
|
|
else {
|
|
ptr = values;
|
|
x = nXBlockSize * iXBlock;
|
|
for (iY = 0; iY < nYValid; iY++) {
|
|
y = iY + (nYBlockSize * iYBlock);
|
|
|
|
RASTER_DEBUGF(4, "Setting pixel line at (%d, %d) for %d pixels", x, y, nXValid);
|
|
rt_band_set_pixel_line(band, x, y, ptr, nXValid);
|
|
ptr += (nXValid * ptlen);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* free memory */
|
|
rtdealloc(values);
|
|
}
|
|
|
|
return rast;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_gdal_rasterize()
|
|
******************************************************************************/
|
|
|
|
typedef struct _rti_rasterize_arg_t* _rti_rasterize_arg;
|
|
struct _rti_rasterize_arg_t {
|
|
uint8_t noband;
|
|
|
|
uint32_t numbands;
|
|
|
|
OGRSpatialReferenceH src_sr;
|
|
|
|
rt_pixtype *pixtype;
|
|
double *init;
|
|
double *nodata;
|
|
uint8_t *hasnodata;
|
|
double *value;
|
|
int *bandlist;
|
|
};
|
|
|
|
static _rti_rasterize_arg
|
|
_rti_rasterize_arg_init() {
|
|
_rti_rasterize_arg arg = NULL;
|
|
|
|
arg = rtalloc(sizeof(struct _rti_rasterize_arg_t));
|
|
if (arg == NULL) {
|
|
rterror("_rti_rasterize_arg_init: Could not allocate memory for _rti_rasterize_arg");
|
|
return NULL;
|
|
}
|
|
|
|
arg->noband = 0;
|
|
|
|
arg->numbands = 0;
|
|
|
|
arg->src_sr = NULL;
|
|
|
|
arg->pixtype = NULL;
|
|
arg->init = NULL;
|
|
arg->nodata = NULL;
|
|
arg->hasnodata = NULL;
|
|
arg->value = NULL;
|
|
arg->bandlist = NULL;
|
|
|
|
return arg;
|
|
}
|
|
|
|
static void
|
|
_rti_rasterize_arg_destroy(_rti_rasterize_arg arg) {
|
|
if (arg->noband) {
|
|
if (arg->pixtype != NULL)
|
|
rtdealloc(arg->pixtype);
|
|
if (arg->init != NULL)
|
|
rtdealloc(arg->init);
|
|
if (arg->nodata != NULL)
|
|
rtdealloc(arg->nodata);
|
|
if (arg->hasnodata != NULL)
|
|
rtdealloc(arg->hasnodata);
|
|
if (arg->value != NULL)
|
|
rtdealloc(arg->value);
|
|
}
|
|
|
|
if (arg->bandlist != NULL)
|
|
rtdealloc(arg->bandlist);
|
|
|
|
if (arg->src_sr != NULL)
|
|
OSRDestroySpatialReference(arg->src_sr);
|
|
|
|
rtdealloc(arg);
|
|
}
|
|
|
|
/**
|
|
* Return a raster of the provided geometry
|
|
*
|
|
* @param wkb : WKB representation of the geometry to convert
|
|
* @param wkb_len : length of the WKB representation of the geometry
|
|
* @param srs : the geometry's coordinate system in OGC WKT
|
|
* @param num_bands : number of bands in the output raster
|
|
* @param pixtype : data type of each band
|
|
* @param init : array of values to initialize each band with
|
|
* @param value : array of values for pixels of geometry
|
|
* @param nodata : array of nodata values for each band
|
|
* @param hasnodata : array flagging the presence of nodata for each band
|
|
* @param width : the number of columns of the raster
|
|
* @param height : the number of rows of the raster
|
|
* @param scale_x : the pixel width of the raster
|
|
* @param scale_y : the pixel height of the raster
|
|
* @param ul_xw : the X value of upper-left corner of the raster
|
|
* @param ul_yw : the Y value of upper-left corner of the raster
|
|
* @param grid_xw : the X value of point on grid to align raster to
|
|
* @param grid_yw : the Y value of point on grid to align raster to
|
|
* @param skew_x : the X skew of the raster
|
|
* @param skew_y : the Y skew of the raster
|
|
* @param options : array of options. only option is "ALL_TOUCHED"
|
|
*
|
|
* @return the raster of the provided geometry or NULL
|
|
*/
|
|
rt_raster
|
|
rt_raster_gdal_rasterize(
|
|
const unsigned char *wkb, uint32_t wkb_len,
|
|
const char *srs,
|
|
uint32_t num_bands, rt_pixtype *pixtype,
|
|
double *init, double *value,
|
|
double *nodata, uint8_t *hasnodata,
|
|
int *width, int *height,
|
|
double *scale_x, double *scale_y,
|
|
double *ul_xw, double *ul_yw,
|
|
double *grid_xw, double *grid_yw,
|
|
double *skew_x, double *skew_y,
|
|
char **options
|
|
) {
|
|
rt_raster rast = NULL;
|
|
int i = 0;
|
|
int err = 0;
|
|
|
|
_rti_rasterize_arg arg = NULL;
|
|
|
|
int _dim[2] = {0};
|
|
double _scale[2] = {0};
|
|
double _skew[2] = {0};
|
|
|
|
OGRErr ogrerr;
|
|
OGRGeometryH src_geom;
|
|
OGREnvelope src_env;
|
|
rt_envelope extent;
|
|
OGRwkbGeometryType wkbtype = wkbUnknown;
|
|
|
|
int ul_user = 0;
|
|
|
|
CPLErr cplerr;
|
|
double _gt[6] = {0};
|
|
GDALDriverH _drv = NULL;
|
|
int unload_drv = 0;
|
|
GDALDatasetH _ds = NULL;
|
|
GDALRasterBandH _band = NULL;
|
|
|
|
uint16_t _width = 0;
|
|
uint16_t _height = 0;
|
|
|
|
RASTER_DEBUG(3, "starting");
|
|
|
|
assert(NULL != wkb);
|
|
assert(0 != wkb_len);
|
|
|
|
/* internal variables */
|
|
arg = _rti_rasterize_arg_init();
|
|
if (arg == NULL) {
|
|
rterror("rt_raster_gdal_rasterize: Could not initialize internal variables");
|
|
return NULL;
|
|
}
|
|
|
|
/* no bands, raster is a mask */
|
|
if (num_bands < 1) {
|
|
arg->noband = 1;
|
|
arg->numbands = 1;
|
|
|
|
arg->pixtype = (rt_pixtype *) rtalloc(sizeof(rt_pixtype));
|
|
arg->pixtype[0] = PT_8BUI;
|
|
|
|
arg->init = (double *) rtalloc(sizeof(double));
|
|
arg->init[0] = 0;
|
|
|
|
arg->nodata = (double *) rtalloc(sizeof(double));
|
|
arg->nodata[0] = 0;
|
|
|
|
arg->hasnodata = (uint8_t *) rtalloc(sizeof(uint8_t));
|
|
arg->hasnodata[0] = 1;
|
|
|
|
arg->value = (double *) rtalloc(sizeof(double));
|
|
arg->value[0] = 1;
|
|
}
|
|
else {
|
|
arg->noband = 0;
|
|
arg->numbands = num_bands;
|
|
|
|
arg->pixtype = pixtype;
|
|
arg->init = init;
|
|
arg->nodata = nodata;
|
|
arg->hasnodata = hasnodata;
|
|
arg->value = value;
|
|
}
|
|
|
|
/* OGR spatial reference */
|
|
if (NULL != srs && strlen(srs)) {
|
|
arg->src_sr = OSRNewSpatialReference(NULL);
|
|
if (OSRSetFromUserInput(arg->src_sr, srs) != OGRERR_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not create OSR spatial reference using the provided srs: %s", srs);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* convert WKB to OGR Geometry */
|
|
ogrerr = OGR_G_CreateFromWkb((unsigned char *) wkb, arg->src_sr, &src_geom, wkb_len);
|
|
if (ogrerr != OGRERR_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not create OGR Geometry from WKB");
|
|
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* OGR Geometry is empty */
|
|
if (OGR_G_IsEmpty(src_geom)) {
|
|
rtinfo("Geometry provided is empty. Returning empty raster");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return rt_raster_new(0, 0);
|
|
}
|
|
|
|
/* get envelope */
|
|
OGR_G_GetEnvelope(src_geom, &src_env);
|
|
rt_util_from_ogr_envelope(src_env, &extent);
|
|
|
|
RASTER_DEBUGF(3, "Suggested raster envelope: %f, %f, %f, %f",
|
|
extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
|
|
|
|
/* user-defined scale */
|
|
if (
|
|
(NULL != scale_x) &&
|
|
(NULL != scale_y) &&
|
|
(FLT_NEQ(*scale_x, 0.0)) &&
|
|
(FLT_NEQ(*scale_y, 0.0))
|
|
) {
|
|
/* for now, force scale to be in left-right, top-down orientation */
|
|
_scale[0] = fabs(*scale_x);
|
|
_scale[1] = fabs(*scale_y);
|
|
}
|
|
/* user-defined width/height */
|
|
else if (
|
|
(NULL != width) &&
|
|
(NULL != height) &&
|
|
(FLT_NEQ(*width, 0.0)) &&
|
|
(FLT_NEQ(*height, 0.0))
|
|
) {
|
|
_dim[0] = abs(*width);
|
|
_dim[1] = abs(*height);
|
|
|
|
if (FLT_NEQ(extent.MaxX, extent.MinX))
|
|
_scale[0] = fabs((extent.MaxX - extent.MinX) / _dim[0]);
|
|
else
|
|
_scale[0] = 1.;
|
|
|
|
if (FLT_NEQ(extent.MaxY, extent.MinY))
|
|
_scale[1] = fabs((extent.MaxY - extent.MinY) / _dim[1]);
|
|
else
|
|
_scale[1] = 1.;
|
|
}
|
|
else {
|
|
rterror("rt_raster_gdal_rasterize: Values must be provided for width and height or X and Y of scale");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
RASTER_DEBUGF(3, "scale (x, y) = %f, %f", _scale[0], -1 * _scale[1]);
|
|
RASTER_DEBUGF(3, "dim (x, y) = %d, %d", _dim[0], _dim[1]);
|
|
|
|
/* user-defined skew */
|
|
if (NULL != skew_x) {
|
|
_skew[0] = *skew_x;
|
|
|
|
/*
|
|
negative scale-x affects skew
|
|
for now, force skew to be in left-right, top-down orientation
|
|
*/
|
|
if (
|
|
NULL != scale_x &&
|
|
*scale_x < 0.
|
|
) {
|
|
_skew[0] *= -1;
|
|
}
|
|
}
|
|
if (NULL != skew_y) {
|
|
_skew[1] = *skew_y;
|
|
|
|
/*
|
|
positive scale-y affects skew
|
|
for now, force skew to be in left-right, top-down orientation
|
|
*/
|
|
if (
|
|
NULL != scale_y &&
|
|
*scale_y > 0.
|
|
) {
|
|
_skew[1] *= -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if geometry is a point, a linestring or set of either and bounds not set,
|
|
increase extent by a pixel to avoid missing points on border
|
|
|
|
a whole pixel is used instead of half-pixel due to backward
|
|
compatibility with GDAL 1.6, 1.7 and 1.8. 1.9+ works fine with half-pixel.
|
|
*/
|
|
wkbtype = wkbFlatten(OGR_G_GetGeometryType(src_geom));
|
|
if ((
|
|
(wkbtype == wkbPoint) ||
|
|
(wkbtype == wkbMultiPoint) ||
|
|
(wkbtype == wkbLineString) ||
|
|
(wkbtype == wkbMultiLineString)
|
|
) &&
|
|
_dim[0] == 0 &&
|
|
_dim[1] == 0
|
|
) {
|
|
int result;
|
|
LWPOLY *epoly = NULL;
|
|
LWGEOM *lwgeom = NULL;
|
|
GEOSGeometry *egeom = NULL;
|
|
GEOSGeometry *geom = NULL;
|
|
|
|
RASTER_DEBUG(3, "Testing geometry is properly contained by extent");
|
|
|
|
/*
|
|
see if geometry is properly contained by extent
|
|
all parts of geometry lies within extent
|
|
*/
|
|
|
|
/* initialize GEOS */
|
|
initGEOS(rtinfo, lwgeom_geos_error);
|
|
|
|
/* convert envelope to geometry */
|
|
RASTER_DEBUG(4, "Converting envelope to geometry");
|
|
epoly = rt_util_envelope_to_lwpoly(extent);
|
|
if (epoly == NULL) {
|
|
rterror("rt_raster_gdal_rasterize: Could not create envelope's geometry to test if geometry is properly contained by extent");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
egeom = (GEOSGeometry *) LWGEOM2GEOS(lwpoly_as_lwgeom(epoly), 0);
|
|
lwpoly_free(epoly);
|
|
|
|
/* convert WKB to geometry */
|
|
RASTER_DEBUG(4, "Converting WKB to geometry");
|
|
lwgeom = lwgeom_from_wkb(wkb, wkb_len, LW_PARSER_CHECK_NONE);
|
|
geom = (GEOSGeometry *) LWGEOM2GEOS(lwgeom, 0);
|
|
lwgeom_free(lwgeom);
|
|
|
|
result = GEOSRelatePattern(egeom, geom, "T**FF*FF*");
|
|
GEOSGeom_destroy(geom);
|
|
GEOSGeom_destroy(egeom);
|
|
|
|
if (result == 2) {
|
|
rterror("rt_raster_gdal_rasterize: Could not test if geometry is properly contained by extent for geometry within extent");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* geometry NOT properly contained by extent */
|
|
if (!result) {
|
|
|
|
#if POSTGIS_GDAL_VERSION > 18
|
|
|
|
/* check alignment flag: grid_xw */
|
|
if (
|
|
(NULL == ul_xw && NULL == ul_yw) &&
|
|
(NULL != grid_xw && NULL != grid_xw) &&
|
|
FLT_NEQ(*grid_xw, extent.MinX)
|
|
) {
|
|
/* do nothing */
|
|
RASTER_DEBUG(3, "Skipping extent adjustment on X-axis due to upcoming alignment");
|
|
}
|
|
else {
|
|
RASTER_DEBUG(3, "Adjusting extent for GDAL > 1.8 by half the scale on X-axis");
|
|
extent.MinX -= (_scale[0] / 2.);
|
|
extent.MaxX += (_scale[0] / 2.);
|
|
}
|
|
|
|
/* check alignment flag: grid_yw */
|
|
if (
|
|
(NULL == ul_xw && NULL == ul_yw) &&
|
|
(NULL != grid_xw && NULL != grid_xw) &&
|
|
FLT_NEQ(*grid_yw, extent.MaxY)
|
|
) {
|
|
/* do nothing */
|
|
RASTER_DEBUG(3, "Skipping extent adjustment on Y-axis due to upcoming alignment");
|
|
}
|
|
else {
|
|
RASTER_DEBUG(3, "Adjusting extent for GDAL > 1.8 by half the scale on Y-axis");
|
|
extent.MinY -= (_scale[1] / 2.);
|
|
extent.MaxY += (_scale[1] / 2.);
|
|
}
|
|
|
|
#else
|
|
|
|
/* check alignment flag: grid_xw */
|
|
if (
|
|
(NULL == ul_xw && NULL == ul_yw) &&
|
|
(NULL != grid_xw && NULL != grid_xw) &&
|
|
FLT_NEQ(*grid_xw, extent.MinX)
|
|
) {
|
|
/* do nothing */
|
|
RASTER_DEBUG(3, "Skipping extent adjustment on X-axis due to upcoming alignment");
|
|
}
|
|
else {
|
|
RASTER_DEBUG(3, "Adjusting extent for GDAL <= 1.8 by the scale on X-axis");
|
|
extent.MinX -= _scale[0];
|
|
extent.MaxX += _scale[0];
|
|
}
|
|
|
|
|
|
/* check alignment flag: grid_yw */
|
|
if (
|
|
(NULL == ul_xw && NULL == ul_yw) &&
|
|
(NULL != grid_xw && NULL != grid_xw) &&
|
|
FLT_NEQ(*grid_yw, extent.MaxY)
|
|
) {
|
|
/* do nothing */
|
|
RASTER_DEBUG(3, "Skipping extent adjustment on Y-axis due to upcoming alignment");
|
|
}
|
|
else {
|
|
RASTER_DEBUG(3, "Adjusting extent for GDAL <= 1.8 by the scale on Y-axis");
|
|
extent.MinY -= _scale[1];
|
|
extent.MaxY += _scale[1];
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
RASTER_DEBUGF(3, "Adjusted extent: %f, %f, %f, %f",
|
|
extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
|
|
|
|
extent.UpperLeftX = extent.MinX;
|
|
extent.UpperLeftY = extent.MaxY;
|
|
}
|
|
|
|
/* reprocess extent if skewed */
|
|
if (
|
|
FLT_NEQ(_skew[0], 0) ||
|
|
FLT_NEQ(_skew[1], 0)
|
|
) {
|
|
rt_raster skewedrast;
|
|
|
|
RASTER_DEBUG(3, "Computing skewed extent's envelope");
|
|
|
|
skewedrast = rt_raster_compute_skewed_raster(
|
|
extent,
|
|
_skew,
|
|
_scale,
|
|
0.01
|
|
);
|
|
if (skewedrast == NULL) {
|
|
rterror("rt_raster_gdal_rasterize: Could not compute skewed raster");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
_dim[0] = skewedrast->width;
|
|
_dim[1] = skewedrast->height;
|
|
|
|
extent.UpperLeftX = skewedrast->ipX;
|
|
extent.UpperLeftY = skewedrast->ipY;
|
|
|
|
rt_raster_destroy(skewedrast);
|
|
}
|
|
|
|
/* raster dimensions */
|
|
if (!_dim[0])
|
|
_dim[0] = (int) fmax((fabs(extent.MaxX - extent.MinX) + (_scale[0] / 2.)) / _scale[0], 1);
|
|
if (!_dim[1])
|
|
_dim[1] = (int) fmax((fabs(extent.MaxY - extent.MinY) + (_scale[1] / 2.)) / _scale[1], 1);
|
|
|
|
/* temporary raster */
|
|
rast = rt_raster_new(_dim[0], _dim[1]);
|
|
if (rast == NULL) {
|
|
rterror("rt_raster_gdal_rasterize: Out of memory allocating temporary raster");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* set raster's spatial attributes */
|
|
rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
|
|
rt_raster_set_scale(rast, _scale[0], -1 * _scale[1]);
|
|
rt_raster_set_skews(rast, _skew[0], _skew[1]);
|
|
|
|
rt_raster_get_geotransform_matrix(rast, _gt);
|
|
RASTER_DEBUGF(3, "Temp raster's geotransform: %f, %f, %f, %f, %f, %f",
|
|
_gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
|
|
RASTER_DEBUGF(3, "Temp raster's dimensions (width x height): %d x %d",
|
|
_dim[0], _dim[1]);
|
|
|
|
/* user-specified upper-left corner */
|
|
if (
|
|
NULL != ul_xw &&
|
|
NULL != ul_yw
|
|
) {
|
|
ul_user = 1;
|
|
|
|
RASTER_DEBUGF(4, "Using user-specified upper-left corner: %f, %f", *ul_xw, *ul_yw);
|
|
|
|
/* set upper-left corner */
|
|
rt_raster_set_offsets(rast, *ul_xw, *ul_yw);
|
|
extent.UpperLeftX = *ul_xw;
|
|
extent.UpperLeftY = *ul_yw;
|
|
}
|
|
else if (
|
|
((NULL != ul_xw) && (NULL == ul_yw)) ||
|
|
((NULL == ul_xw) && (NULL != ul_yw))
|
|
) {
|
|
rterror("rt_raster_gdal_rasterize: Both X and Y upper-left corner values must be provided");
|
|
|
|
rt_raster_destroy(rast);
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* alignment only considered if upper-left corner not provided */
|
|
if (
|
|
!ul_user && (
|
|
(NULL != grid_xw) || (NULL != grid_yw)
|
|
)
|
|
) {
|
|
|
|
if (
|
|
((NULL != grid_xw) && (NULL == grid_yw)) ||
|
|
((NULL == grid_xw) && (NULL != grid_yw))
|
|
) {
|
|
rterror("rt_raster_gdal_rasterize: Both X and Y alignment values must be provided");
|
|
|
|
rt_raster_destroy(rast);
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
RASTER_DEBUGF(4, "Aligning extent to user-specified grid: %f, %f", *grid_xw, *grid_yw);
|
|
|
|
do {
|
|
double _r[2] = {0};
|
|
double _w[2] = {0};
|
|
|
|
/* raster is already aligned */
|
|
if (FLT_EQ(*grid_xw, extent.UpperLeftX) && FLT_EQ(*grid_yw, extent.UpperLeftY)) {
|
|
RASTER_DEBUG(3, "Skipping raster alignment as it is already aligned to grid");
|
|
break;
|
|
}
|
|
|
|
extent.UpperLeftX = rast->ipX;
|
|
extent.UpperLeftY = rast->ipY;
|
|
rt_raster_set_offsets(rast, *grid_xw, *grid_yw);
|
|
|
|
/* process upper-left corner */
|
|
if (rt_raster_geopoint_to_cell(
|
|
rast,
|
|
extent.UpperLeftX, extent.UpperLeftY,
|
|
&(_r[0]), &(_r[1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not compute raster pixel for spatial coordinates");
|
|
|
|
rt_raster_destroy(rast);
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (rt_raster_cell_to_geopoint(
|
|
rast,
|
|
_r[0], _r[1],
|
|
&(_w[0]), &(_w[1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
|
|
|
|
rt_raster_destroy(rast);
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* shift occurred */
|
|
if (FLT_NEQ(_w[0], extent.UpperLeftX)) {
|
|
if (NULL == width)
|
|
rast->width++;
|
|
else if (NULL == scale_x) {
|
|
double _c[2] = {0};
|
|
|
|
rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
|
|
|
|
/* get upper-right corner */
|
|
if (rt_raster_cell_to_geopoint(
|
|
rast,
|
|
rast->width, 0,
|
|
&(_c[0]), &(_c[1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
|
|
|
|
rt_raster_destroy(rast);
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
rast->scaleX = fabs((_c[0] - _w[0]) / ((double) rast->width));
|
|
}
|
|
}
|
|
if (FLT_NEQ(_w[1], extent.UpperLeftY)) {
|
|
if (NULL == height)
|
|
rast->height++;
|
|
else if (NULL == scale_y) {
|
|
double _c[2] = {0};
|
|
|
|
rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
|
|
|
|
/* get upper-right corner */
|
|
if (rt_raster_cell_to_geopoint(
|
|
rast,
|
|
0, rast->height,
|
|
&(_c[0]), &(_c[1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
|
|
|
|
rt_raster_destroy(rast);
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
rast->scaleY = -1 * fabs((_c[1] - _w[1]) / ((double) rast->height));
|
|
}
|
|
}
|
|
|
|
rt_raster_set_offsets(rast, _w[0], _w[1]);
|
|
}
|
|
while (0);
|
|
}
|
|
|
|
/*
|
|
after this point, rt_envelope extent is no longer used
|
|
*/
|
|
|
|
/* get key attributes from rast */
|
|
_dim[0] = rast->width;
|
|
_dim[1] = rast->height;
|
|
rt_raster_get_geotransform_matrix(rast, _gt);
|
|
|
|
/* scale-x is negative or scale-y is positive */
|
|
if ((
|
|
(NULL != scale_x) && (*scale_x < 0.)
|
|
) || (
|
|
(NULL != scale_y) && (*scale_y > 0)
|
|
)) {
|
|
double _w[2] = {0};
|
|
|
|
/* negative scale-x */
|
|
if (
|
|
(NULL != scale_x) &&
|
|
(*scale_x < 0.)
|
|
) {
|
|
RASTER_DEBUG(3, "Processing negative scale-x");
|
|
|
|
if (rt_raster_cell_to_geopoint(
|
|
rast,
|
|
_dim[0], 0,
|
|
&(_w[0]), &(_w[1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
|
|
|
|
rt_raster_destroy(rast);
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
_gt[0] = _w[0];
|
|
_gt[1] = *scale_x;
|
|
|
|
/* check for skew */
|
|
if (NULL != skew_x && FLT_NEQ(*skew_x, 0))
|
|
_gt[2] = *skew_x;
|
|
}
|
|
/* positive scale-y */
|
|
if (
|
|
(NULL != scale_y) &&
|
|
(*scale_y > 0)
|
|
) {
|
|
RASTER_DEBUG(3, "Processing positive scale-y");
|
|
|
|
if (rt_raster_cell_to_geopoint(
|
|
rast,
|
|
0, _dim[1],
|
|
&(_w[0]), &(_w[1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
|
|
|
|
rt_raster_destroy(rast);
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
_gt[3] = _w[1];
|
|
_gt[5] = *scale_y;
|
|
|
|
/* check for skew */
|
|
if (NULL != skew_y && FLT_NEQ(*skew_y, 0))
|
|
_gt[4] = *skew_y;
|
|
}
|
|
}
|
|
|
|
rt_raster_destroy(rast);
|
|
rast = NULL;
|
|
|
|
RASTER_DEBUGF(3, "Applied geotransform: %f, %f, %f, %f, %f, %f",
|
|
_gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
|
|
RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d",
|
|
_dim[0], _dim[1]);
|
|
|
|
/* load GDAL mem */
|
|
if (!rt_util_gdal_driver_registered("MEM")) {
|
|
RASTER_DEBUG(4, "Registering MEM driver");
|
|
GDALRegister_MEM();
|
|
unload_drv = 1;
|
|
}
|
|
_drv = GDALGetDriverByName("MEM");
|
|
if (NULL == _drv) {
|
|
rterror("rt_raster_gdal_rasterize: Could not load the MEM GDAL driver");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* unload driver from GDAL driver manager */
|
|
if (unload_drv) {
|
|
RASTER_DEBUG(4, "Deregistering MEM driver");
|
|
GDALDeregisterDriver(_drv);
|
|
}
|
|
|
|
_ds = GDALCreate(_drv, "", _dim[0], _dim[1], 0, GDT_Byte, NULL);
|
|
if (NULL == _ds) {
|
|
rterror("rt_raster_gdal_rasterize: Could not create a GDALDataset to rasterize the geometry into");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
if (unload_drv) GDALDestroyDriver(_drv);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* set geotransform */
|
|
cplerr = GDALSetGeoTransform(_ds, _gt);
|
|
if (cplerr != CE_None) {
|
|
rterror("rt_raster_gdal_rasterize: Could not set geotransform on GDALDataset");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
GDALClose(_ds);
|
|
if (unload_drv) GDALDestroyDriver(_drv);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* set SRS */
|
|
if (NULL != arg->src_sr) {
|
|
char *_srs = NULL;
|
|
OSRExportToWkt(arg->src_sr, &_srs);
|
|
|
|
cplerr = GDALSetProjection(_ds, _srs);
|
|
CPLFree(_srs);
|
|
if (cplerr != CE_None) {
|
|
rterror("rt_raster_gdal_rasterize: Could not set projection on GDALDataset");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
GDALClose(_ds);
|
|
if (unload_drv) GDALDestroyDriver(_drv);
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* set bands */
|
|
for (i = 0; i < arg->numbands; i++) {
|
|
err = 0;
|
|
|
|
do {
|
|
/* add band */
|
|
cplerr = GDALAddBand(_ds, rt_util_pixtype_to_gdal_datatype(arg->pixtype[i]), NULL);
|
|
if (cplerr != CE_None) {
|
|
rterror("rt_raster_gdal_rasterize: Could not add band to GDALDataset");
|
|
err = 1;
|
|
break;
|
|
}
|
|
|
|
_band = GDALGetRasterBand(_ds, i + 1);
|
|
if (NULL == _band) {
|
|
rterror("rt_raster_gdal_rasterize: Could not get band %d from GDALDataset", i + 1);
|
|
err = 1;
|
|
break;
|
|
}
|
|
|
|
/* nodata value */
|
|
if (arg->hasnodata[i]) {
|
|
RASTER_DEBUGF(4, "Setting NODATA value of band %d to %f", i, arg->nodata[i]);
|
|
cplerr = GDALSetRasterNoDataValue(_band, arg->nodata[i]);
|
|
if (cplerr != CE_None) {
|
|
rterror("rt_raster_gdal_rasterize: Could not set nodata value");
|
|
err = 1;
|
|
break;
|
|
}
|
|
RASTER_DEBUGF(4, "NODATA value set to %f", GDALGetRasterNoDataValue(_band, NULL));
|
|
}
|
|
|
|
/* initial value */
|
|
cplerr = GDALFillRaster(_band, arg->init[i], 0);
|
|
if (cplerr != CE_None) {
|
|
rterror("rt_raster_gdal_rasterize: Could not set initial value");
|
|
err = 1;
|
|
break;
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
if (err) {
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
|
|
/* OGRCleanupAll(); */
|
|
|
|
GDALClose(_ds);
|
|
if (unload_drv) GDALDestroyDriver(_drv);
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
arg->bandlist = (int *) rtalloc(sizeof(int) * arg->numbands);
|
|
for (i = 0; i < arg->numbands; i++) arg->bandlist[i] = i + 1;
|
|
|
|
/* burn geometry */
|
|
cplerr = GDALRasterizeGeometries(
|
|
_ds,
|
|
arg->numbands, arg->bandlist,
|
|
1, &src_geom,
|
|
NULL, NULL,
|
|
arg->value,
|
|
options,
|
|
NULL, NULL
|
|
);
|
|
if (cplerr != CE_None) {
|
|
rterror("rt_raster_gdal_rasterize: Could not rasterize geometry");
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
/* OGRCleanupAll(); */
|
|
|
|
GDALClose(_ds);
|
|
if (unload_drv) GDALDestroyDriver(_drv);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* convert gdal dataset to raster */
|
|
GDALFlushCache(_ds);
|
|
RASTER_DEBUG(3, "Converting GDAL dataset to raster");
|
|
rast = rt_raster_from_gdal_dataset(_ds);
|
|
|
|
OGR_G_DestroyGeometry(src_geom);
|
|
/* OGRCleanupAll(); */
|
|
|
|
GDALClose(_ds);
|
|
if (unload_drv) GDALDestroyDriver(_drv);
|
|
|
|
if (NULL == rast) {
|
|
rterror("rt_raster_gdal_rasterize: Could not rasterize geometry");
|
|
return NULL;
|
|
}
|
|
|
|
/* width, height */
|
|
_width = rt_raster_get_width(rast);
|
|
_height = rt_raster_get_height(rast);
|
|
|
|
/* check each band for pixtype */
|
|
for (i = 0; i < arg->numbands; i++) {
|
|
uint8_t *data = NULL;
|
|
rt_band band = NULL;
|
|
rt_band oldband = NULL;
|
|
|
|
double val = 0;
|
|
int nodata = 0;
|
|
int hasnodata = 0;
|
|
double nodataval = 0;
|
|
int x = 0;
|
|
int y = 0;
|
|
|
|
oldband = rt_raster_get_band(rast, i);
|
|
if (oldband == NULL) {
|
|
rterror("rt_raster_gdal_rasterize: Could not get band %d of output raster", i);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
rt_raster_destroy(rast);
|
|
return NULL;
|
|
}
|
|
|
|
/* band is of user-specified type */
|
|
if (rt_band_get_pixtype(oldband) == arg->pixtype[i])
|
|
continue;
|
|
|
|
/* hasnodata, nodataval */
|
|
hasnodata = rt_band_get_hasnodata_flag(oldband);
|
|
if (hasnodata)
|
|
rt_band_get_nodata(oldband, &nodataval);
|
|
|
|
/* allocate data */
|
|
data = rtalloc(rt_pixtype_size(arg->pixtype[i]) * _width * _height);
|
|
if (data == NULL) {
|
|
rterror("rt_raster_gdal_rasterize: Could not allocate memory for band data");
|
|
_rti_rasterize_arg_destroy(arg);
|
|
rt_raster_destroy(rast);
|
|
return NULL;
|
|
}
|
|
memset(data, 0, rt_pixtype_size(arg->pixtype[i]) * _width * _height);
|
|
|
|
/* create new band of correct type */
|
|
band = rt_band_new_inline(
|
|
_width, _height,
|
|
arg->pixtype[i],
|
|
hasnodata, nodataval,
|
|
data
|
|
);
|
|
if (band == NULL) {
|
|
rterror("rt_raster_gdal_rasterize: Could not create band");
|
|
rtdealloc(data);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
rt_raster_destroy(rast);
|
|
return NULL;
|
|
}
|
|
|
|
/* give ownership of data to band */
|
|
rt_band_set_ownsdata_flag(band, 1);
|
|
|
|
/* copy pixel by pixel */
|
|
for (x = 0; x < _width; x++) {
|
|
for (y = 0; y < _height; y++) {
|
|
err = rt_band_get_pixel(oldband, x, y, &val, &nodata);
|
|
if (err != ES_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not get pixel value");
|
|
_rti_rasterize_arg_destroy(arg);
|
|
rt_raster_destroy(rast);
|
|
rt_band_destroy(band);
|
|
return NULL;
|
|
}
|
|
|
|
if (nodata)
|
|
val = nodataval;
|
|
|
|
err = rt_band_set_pixel(band, x, y, val, NULL);
|
|
if (err != ES_NONE) {
|
|
rterror("rt_raster_gdal_rasterize: Could not set pixel value");
|
|
_rti_rasterize_arg_destroy(arg);
|
|
rt_raster_destroy(rast);
|
|
rt_band_destroy(band);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* replace band */
|
|
oldband = rt_raster_replace_band(rast, band, i);
|
|
if (oldband == NULL) {
|
|
rterror("rt_raster_gdal_rasterize: Could not replace band %d of output raster", i);
|
|
_rti_rasterize_arg_destroy(arg);
|
|
rt_raster_destroy(rast);
|
|
rt_band_destroy(band);
|
|
return NULL;
|
|
}
|
|
|
|
/* free oldband */
|
|
rt_band_destroy(oldband);
|
|
}
|
|
|
|
_rti_rasterize_arg_destroy(arg);
|
|
|
|
RASTER_DEBUG(3, "done");
|
|
|
|
return rast;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* rt_raster_from_two_rasters()
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* Return raster of computed extent specified extenttype applied
|
|
* on two input rasters. The raster returned should be freed by
|
|
* the caller
|
|
*
|
|
* @param rast1 : the first raster
|
|
* @param rast2 : the second raster
|
|
* @param extenttype : type of extent for the output raster
|
|
* @param *rtnraster : raster of computed extent
|
|
* @param *offset : 4-element array indicating the X,Y offsets
|
|
* for each raster. 0,1 for rast1 X,Y. 2,3 for rast2 X,Y.
|
|
*
|
|
* @return ES_NONE if success, ES_ERROR if error
|
|
*/
|
|
rt_errorstate
|
|
rt_raster_from_two_rasters(
|
|
rt_raster rast1, rt_raster rast2,
|
|
rt_extenttype extenttype,
|
|
rt_raster *rtnraster, double *offset
|
|
) {
|
|
int i;
|
|
|
|
rt_raster _rast[2] = {rast1, rast2};
|
|
double _offset[2][4] = {{0.}};
|
|
uint16_t _dim[2][2] = {{0}};
|
|
|
|
rt_raster raster = NULL;
|
|
int aligned = 0;
|
|
int dim[2] = {0};
|
|
double gt[6] = {0.};
|
|
|
|
assert(NULL != rast1);
|
|
assert(NULL != rast2);
|
|
assert(NULL != rtnraster);
|
|
|
|
/* set rtnraster to NULL */
|
|
*rtnraster = NULL;
|
|
|
|
/* rasters must be aligned */
|
|
if (rt_raster_same_alignment(rast1, rast2, &aligned, NULL) != ES_NONE) {
|
|
rterror("rt_raster_from_two_rasters: Could not test for alignment on the two rasters");
|
|
return ES_ERROR;
|
|
}
|
|
if (!aligned) {
|
|
rterror("rt_raster_from_two_rasters: The two rasters provided do not have the same alignment");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
/* dimensions */
|
|
_dim[0][0] = rast1->width;
|
|
_dim[0][1] = rast1->height;
|
|
_dim[1][0] = rast2->width;
|
|
_dim[1][1] = rast2->height;
|
|
|
|
/* get raster offsets */
|
|
if (rt_raster_geopoint_to_cell(
|
|
_rast[1],
|
|
_rast[0]->ipX, _rast[0]->ipY,
|
|
&(_offset[1][0]), &(_offset[1][1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_from_two_rasters: Could not compute offsets of the second raster relative to the first raster");
|
|
return ES_ERROR;
|
|
}
|
|
_offset[1][0] = -1 * _offset[1][0];
|
|
_offset[1][1] = -1 * _offset[1][1];
|
|
_offset[1][2] = _offset[1][0] + _dim[1][0] - 1;
|
|
_offset[1][3] = _offset[1][1] + _dim[1][1] - 1;
|
|
|
|
i = -1;
|
|
switch (extenttype) {
|
|
case ET_FIRST:
|
|
i = 0;
|
|
_offset[0][0] = 0.;
|
|
_offset[0][1] = 0.;
|
|
case ET_LAST:
|
|
case ET_SECOND:
|
|
if (i < 0) {
|
|
i = 1;
|
|
_offset[0][0] = -1 * _offset[1][0];
|
|
_offset[0][1] = -1 * _offset[1][1];
|
|
_offset[1][0] = 0.;
|
|
_offset[1][1] = 0.;
|
|
}
|
|
|
|
dim[0] = _dim[i][0];
|
|
dim[1] = _dim[i][1];
|
|
raster = rt_raster_new(
|
|
dim[0],
|
|
dim[1]
|
|
);
|
|
if (raster == NULL) {
|
|
rterror("rt_raster_from_two_rasters: Could not create output raster");
|
|
return ES_ERROR;
|
|
}
|
|
rt_raster_set_srid(raster, _rast[i]->srid);
|
|
rt_raster_get_geotransform_matrix(_rast[i], gt);
|
|
rt_raster_set_geotransform_matrix(raster, gt);
|
|
break;
|
|
case ET_UNION: {
|
|
double off[4] = {0};
|
|
|
|
rt_raster_get_geotransform_matrix(_rast[0], gt);
|
|
RASTER_DEBUGF(4, "gt = (%f, %f, %f, %f, %f, %f)",
|
|
gt[0],
|
|
gt[1],
|
|
gt[2],
|
|
gt[3],
|
|
gt[4],
|
|
gt[5]
|
|
);
|
|
|
|
/* new raster upper left offset */
|
|
off[0] = 0;
|
|
if (_offset[1][0] < 0)
|
|
off[0] = _offset[1][0];
|
|
off[1] = 0;
|
|
if (_offset[1][1] < 0)
|
|
off[1] = _offset[1][1];
|
|
|
|
/* new raster lower right offset */
|
|
off[2] = _dim[0][0] - 1;
|
|
if ((int) _offset[1][2] >= _dim[0][0])
|
|
off[2] = _offset[1][2];
|
|
off[3] = _dim[0][1] - 1;
|
|
if ((int) _offset[1][3] >= _dim[0][1])
|
|
off[3] = _offset[1][3];
|
|
|
|
/* upper left corner */
|
|
if (rt_raster_cell_to_geopoint(
|
|
_rast[0],
|
|
off[0], off[1],
|
|
&(gt[0]), &(gt[3]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_from_two_rasters: Could not get spatial coordinates of upper-left pixel of output raster");
|
|
return ES_ERROR;
|
|
}
|
|
|
|
dim[0] = off[2] - off[0] + 1;
|
|
dim[1] = off[3] - off[1] + 1;
|
|
RASTER_DEBUGF(4, "off = (%f, %f, %f, %f)",
|
|
off[0],
|
|
off[1],
|
|
off[2],
|
|
off[3]
|
|
);
|
|
RASTER_DEBUGF(4, "dim = (%d, %d)", dim[0], dim[1]);
|
|
|
|
raster = rt_raster_new(
|
|
dim[0],
|
|
dim[1]
|
|
);
|
|
if (raster == NULL) {
|
|
rterror("rt_raster_from_two_rasters: Could not create output raster");
|
|
return ES_ERROR;
|
|
}
|
|
rt_raster_set_srid(raster, _rast[0]->srid);
|
|
rt_raster_set_geotransform_matrix(raster, gt);
|
|
RASTER_DEBUGF(4, "gt = (%f, %f, %f, %f, %f, %f)",
|
|
gt[0],
|
|
gt[1],
|
|
gt[2],
|
|
gt[3],
|
|
gt[4],
|
|
gt[5]
|
|
);
|
|
|
|
/* get offsets */
|
|
if (rt_raster_geopoint_to_cell(
|
|
_rast[0],
|
|
gt[0], gt[3],
|
|
&(_offset[0][0]), &(_offset[0][1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_from_two_rasters: Could not get offsets of the FIRST raster relative to the output raster");
|
|
rt_raster_destroy(raster);
|
|
return ES_ERROR;
|
|
}
|
|
_offset[0][0] *= -1;
|
|
_offset[0][1] *= -1;
|
|
|
|
if (rt_raster_geopoint_to_cell(
|
|
_rast[1],
|
|
gt[0], gt[3],
|
|
&(_offset[1][0]), &(_offset[1][1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_from_two_rasters: Could not get offsets of the SECOND raster relative to the output raster");
|
|
rt_raster_destroy(raster);
|
|
return ES_ERROR;
|
|
}
|
|
_offset[1][0] *= -1;
|
|
_offset[1][1] *= -1;
|
|
break;
|
|
}
|
|
case ET_INTERSECTION: {
|
|
double off[4] = {0};
|
|
|
|
/* no intersection */
|
|
if (
|
|
(_offset[1][2] < 0 || _offset[1][0] > (_dim[0][0] - 1)) ||
|
|
(_offset[1][3] < 0 || _offset[1][1] > (_dim[0][1] - 1))
|
|
) {
|
|
RASTER_DEBUG(3, "The two rasters provided have no intersection. Returning no band raster");
|
|
|
|
raster = rt_raster_new(0, 0);
|
|
if (raster == NULL) {
|
|
rterror("rt_raster_from_two_rasters: Could not create output raster");
|
|
return ES_ERROR;
|
|
}
|
|
rt_raster_set_srid(raster, _rast[0]->srid);
|
|
rt_raster_set_scale(raster, 0, 0);
|
|
|
|
/* set offsets if provided */
|
|
if (NULL != offset) {
|
|
for (i = 0; i < 4; i++)
|
|
offset[i] = _offset[i / 2][i % 2];
|
|
}
|
|
|
|
*rtnraster = raster;
|
|
return ES_NONE;
|
|
}
|
|
|
|
if (_offset[1][0] > 0)
|
|
off[0] = _offset[1][0];
|
|
if (_offset[1][1] > 0)
|
|
off[1] = _offset[1][1];
|
|
|
|
off[2] = _dim[0][0] - 1;
|
|
if (_offset[1][2] < _dim[0][0])
|
|
off[2] = _offset[1][2];
|
|
off[3] = _dim[0][1] - 1;
|
|
if (_offset[1][3] < _dim[0][1])
|
|
off[3] = _offset[1][3];
|
|
|
|
dim[0] = off[2] - off[0] + 1;
|
|
dim[1] = off[3] - off[1] + 1;
|
|
raster = rt_raster_new(
|
|
dim[0],
|
|
dim[1]
|
|
);
|
|
if (raster == NULL) {
|
|
rterror("rt_raster_from_two_rasters: Could not create output raster");
|
|
return ES_ERROR;
|
|
}
|
|
rt_raster_set_srid(raster, _rast[0]->srid);
|
|
|
|
/* get upper-left corner */
|
|
rt_raster_get_geotransform_matrix(_rast[0], gt);
|
|
if (rt_raster_cell_to_geopoint(
|
|
_rast[0],
|
|
off[0], off[1],
|
|
&(gt[0]), &(gt[3]),
|
|
gt
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_from_two_rasters: Could not get spatial coordinates of upper-left pixel of output raster");
|
|
rt_raster_destroy(raster);
|
|
return ES_ERROR;
|
|
}
|
|
|
|
rt_raster_set_geotransform_matrix(raster, gt);
|
|
|
|
/* get offsets */
|
|
if (rt_raster_geopoint_to_cell(
|
|
_rast[0],
|
|
gt[0], gt[3],
|
|
&(_offset[0][0]), &(_offset[0][1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_from_two_rasters: Could not get pixel coordinates to compute the offsets of the FIRST raster relative to the output raster");
|
|
rt_raster_destroy(raster);
|
|
return ES_ERROR;
|
|
}
|
|
_offset[0][0] *= -1;
|
|
_offset[0][1] *= -1;
|
|
|
|
if (rt_raster_geopoint_to_cell(
|
|
_rast[1],
|
|
gt[0], gt[3],
|
|
&(_offset[1][0]), &(_offset[1][1]),
|
|
NULL
|
|
) != ES_NONE) {
|
|
rterror("rt_raster_from_two_rasters: Could not get pixel coordinates to compute the offsets of the SECOND raster relative to the output raster");
|
|
rt_raster_destroy(raster);
|
|
return ES_ERROR;
|
|
}
|
|
_offset[1][0] *= -1;
|
|
_offset[1][1] *= -1;
|
|
break;
|
|
}
|
|
case ET_CUSTOM:
|
|
rterror("rt_raster_from_two_rasters: Extent type ET_CUSTOM is not supported by this function");
|
|
break;
|
|
}
|
|
|
|
/* set offsets if provided */
|
|
if (NULL != offset) {
|
|
for (i = 0; i < 4; i++)
|
|
offset[i] = _offset[i / 2][i % 2];
|
|
}
|
|
|
|
*rtnraster = raster;
|
|
return ES_NONE;
|
|
}
|