Create a new empty raster with having the same georeference as the provided raster
If this new raster is empty (width = 0 OR height = 0) then there is nothing to compute and we return it right now
Check if the raster has the required band. Otherwise, return a raster without band
We set the initial value of the future band to nodata value. If nodata value is null, then the raster will be initialized to rt_band_get_min_value but all the values should be recomputed anyway
Optimization: If a nodataval is provided, use it for newinitialvalue. Then, we can initialize the raster with this value and skip the computation of nodata values one by one in the main computing loop
Optimization: If the raster is only filled with nodata values return right now a raster filled with the newinitialvalue TODO: Call rt_band_check_isnodata instead?
Optimization: If expression resume to 'RAST' and hasnodataval is zero, we can just return the band from the original raster
Create the raster receiving all the computed values. Initialize it to the new initial value
Optimization: If expression is NULL, or all the pixels could be set in one step, return the initialized raster now
We compute a value only for the withdata value pixel since the nodata value has already been set by the first optimization
4475{
4482 int x,
y,
nband, width, height;
4484 double newnodatavalue = 0.0;
4485 double newinitialvalue = 0.0;
4486 double newval = 0.0;
4487 char *newexpr = NULL;
4488 char *initexpr = NULL;
4489 char *expression = NULL;
4490 int hasnodataval = 0;
4491 double nodataval = 0.;
4493 int skipcomputation = 0;
4494 int len = 0;
4495 const int argkwcount = 3;
4496 enum KEYWORDS { kVAL=0, kX=1, kY=2 };
4497 char *argkw[] = {"[rast]", "[rast.x]", "[rast.y]"};
4498 Oid argkwtypes[] = { FLOAT8OID, INT4OID, INT4OID };
4499 int argcount = 0;
4500 Oid argtype[] = { FLOAT8OID, INT4OID, INT4OID };
4501 uint8_t argpos[3] = {0};
4502 char place[12];
4503 int idx = 0;
4504 int ret = -1;
4505 TupleDesc tupdesc;
4506 SPIPlanPtr spi_plan = NULL;
4507 SPITupleTable * tuptable = NULL;
4508 HeapTuple tuple;
4509 char * strFromText = NULL;
4510 Datum *values = NULL;
4511 Datum datum = (Datum)NULL;
4512 char *nulls = NULL;
4513 bool isnull =
FALSE;
4514 int i = 0;
4515 int j = 0;
4516
4518
4519
4520 if (PG_ARGISNULL(0)) {
4521 elog(NOTICE, "Raster is NULL. Returning NULL");
4522 PG_RETURN_NULL();
4523 }
4524
4525
4526
4527 pgraster = (
rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
4529 if (NULL == raster) {
4530 PG_FREE_IF_COPY(pgraster, 0);
4531 elog(ERROR, "RASTER_mapAlgebraExpr: Could not deserialize raster");
4532 PG_RETURN_NULL();
4533 }
4534
4536
4537 if (PG_ARGISNULL(1))
4539 else
4540 nband = PG_GETARG_INT32(1);
4541
4542 if (nband < 1)
4544
4545
4546 POSTGIS_RT_DEBUG(3,
"RASTER_mapAlgebraExpr: Creating new empty raster...");
4547
4554
4556
4557 if ( NULL == newrast ) {
4558 PG_FREE_IF_COPY(pgraster, 0);
4559 elog(ERROR, "RASTER_mapAlgebraExpr: Could not create a new raster");
4560 PG_RETURN_NULL();
4561 }
4562
4566
4570
4574
4576
4577
4583 {
4584 elog(NOTICE, "Raster is empty. Returning an empty raster");
4586 PG_FREE_IF_COPY(pgraster, 0);
4587
4590 if (NULL == pgrtn) {
4591
4592 elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
4593 PG_RETURN_NULL();
4594 }
4595
4596 SET_VARSIZE(pgrtn, pgrtn->
size);
4597 PG_RETURN_POINTER(pgrtn);
4598 }
4599
4600
4601 POSTGIS_RT_DEBUGF(3,
"RASTER_mapAlgebraExpr: Getting raster band %d...", nband);
4602
4608 elog(NOTICE, "Raster does not have the required band. Returning a raster "
4609 "without a band");
4611 PG_FREE_IF_COPY(pgraster, 0);
4612
4615 if (NULL == pgrtn) {
4616 elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
4617 PG_RETURN_NULL();
4618 }
4619
4620 SET_VARSIZE(pgrtn, pgrtn->
size);
4621 PG_RETURN_POINTER(pgrtn);
4622 }
4623
4624
4626 if ( NULL == band ) {
4627 elog(NOTICE, "Could not get the required band. Returning a raster "
4628 "without a band");
4630 PG_FREE_IF_COPY(pgraster, 0);
4631
4634 if (NULL == pgrtn) {
4635 elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
4636 PG_RETURN_NULL();
4637 }
4638
4639 SET_VARSIZE(pgrtn, pgrtn->
size);
4640 PG_RETURN_POINTER(pgrtn);
4641 }
4642
4643
4644
4645
4646 POSTGIS_RT_DEBUG(3,
"RASTER_mapAlgebraExpr: Getting NODATA value for band...");
4647
4650 }
4651
4652 else {
4654 }
4655
4657 newnodatavalue);
4658
4664 newinitialvalue = newnodatavalue;
4665
4670
4671 if (PG_ARGISNULL(2)) {
4673 }
4674
4675 else {
4678 pfree(strFromText);
4679 if (newpixeltype ==
PT_END)
4681 }
4682
4683 if (newpixeltype ==
PT_END) {
4684 PG_FREE_IF_COPY(pgraster, 0);
4685 elog(ERROR, "RASTER_mapAlgebraExpr: Invalid pixeltype");
4686 PG_RETURN_NULL();
4687 }
4688
4691
4692
4693
4694 if (!PG_ARGISNULL(3)) {
4696 len = strlen("SELECT (") + strlen(expression) + strlen(")::double precision");
4697 initexpr = (char *)palloc(len + 1);
4698
4699 memcpy(initexpr, "SELECT (", strlen("SELECT ("));
4700 memcpy(initexpr + strlen("SELECT ("), expression, strlen(expression));
4701 memcpy(initexpr + strlen("SELECT (") + strlen(expression), ")::double precision", strlen(")::double precision"));
4702 initexpr[len] = '\0';
4703
4705
4706
4707
4708
4709
4710
4711 }
4712
4713
4714
4720 if (!PG_ARGISNULL(4)) {
4721 hasnodataval = 1;
4722 nodataval = PG_GETARG_FLOAT8(4);
4723 newinitialvalue = nodataval;
4724
4726 newinitialvalue);
4727 }
4728 else
4729 hasnodataval = 0;
4730
4731
4732
4739
4740 POSTGIS_RT_DEBUG(3,
"RASTER_mapAlgebraExpr: Band is a nodata band, returning "
4741 "a raster filled with nodata");
4742
4744 newinitialvalue,
TRUE, newnodatavalue, 0);
4745
4746
4747 if (initexpr)
4748 pfree(initexpr);
4750 PG_FREE_IF_COPY(pgraster, 0);
4751
4752
4755 if (NULL == pgrtn) {
4756 elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
4757 PG_RETURN_NULL();
4758 }
4759
4760 SET_VARSIZE(pgrtn, pgrtn->
size);
4761 PG_RETURN_POINTER(pgrtn);
4762 }
4763
4764
4769 if (initexpr != NULL && ( !strcmp(initexpr, "SELECT [rast]") || !strcmp(initexpr, "SELECT [rast.val]") ) && !hasnodataval) {
4770
4772 "Returning raster with band %d from original raster", nband);
4773
4776
4778
4781
4782 pfree(initexpr);
4784 PG_FREE_IF_COPY(pgraster, 0);
4785
4786
4789 if (NULL == pgrtn) {
4790 elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
4791 PG_RETURN_NULL();
4792 }
4793
4794 SET_VARSIZE(pgrtn, pgrtn->
size);
4795 PG_RETURN_POINTER(pgrtn);
4796 }
4797
4802 if (initexpr != NULL && strstr(initexpr, "[rast") == NULL) {
4803 ret = SPI_connect();
4804 if (ret != SPI_OK_CONNECT) {
4805 PG_FREE_IF_COPY(pgraster, 0);
4806 elog(ERROR, "RASTER_mapAlgebraExpr: Could not connect to the SPI manager");
4807 PG_RETURN_NULL();
4808 };
4809
4810
4811 ret = SPI_execute(initexpr,
FALSE, 0);
4812
4813 if (ret != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
4814
4815
4816 if (SPI_tuptable)
4817 SPI_freetuptable(tuptable);
4818 PG_FREE_IF_COPY(pgraster, 0);
4819
4820 SPI_finish();
4821 elog(ERROR, "RASTER_mapAlgebraExpr: Invalid construction for expression");
4822 PG_RETURN_NULL();
4823 }
4824
4825 tupdesc = SPI_tuptable->tupdesc;
4826 tuptable = SPI_tuptable;
4827
4828 tuple = tuptable->vals[0];
4829 newexpr = SPI_getvalue(tuple, tupdesc, 1);
4830 if ( ! newexpr ) {
4831 POSTGIS_RT_DEBUG(3,
"Constant expression evaluated to NULL, keeping initvalue");
4832 newval = newinitialvalue;
4833 } else {
4834 newval = atof(newexpr);
4835 }
4836
4837 SPI_freetuptable(tuptable);
4838
4840 newval);
4841
4842 SPI_finish();
4843
4844 skipcomputation = 1;
4845
4850 if (!hasnodataval) {
4851 newinitialvalue = newval;
4852 skipcomputation = 2;
4853 }
4854
4855
4856 else if (
FLT_NEQ(newval, newinitialvalue)) {
4857 skipcomputation = 2;
4858 }
4859 }
4860
4866 newinitialvalue,
TRUE, newnodatavalue, 0);
4867
4872
4873 if (expression == NULL || skipcomputation == 2) {
4874
4875
4876 if (initexpr)
4877 pfree(initexpr);
4879 PG_FREE_IF_COPY(pgraster, 0);
4880
4881
4884 if (NULL == pgrtn) {
4885 elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
4886 PG_RETURN_NULL();
4887 }
4888
4889 SET_VARSIZE(pgrtn, pgrtn->
size);
4890 PG_RETURN_POINTER(pgrtn);
4891 }
4892
4893 RASTER_DEBUG(3,
"RASTER_mapAlgebraExpr: Creating new raster band...");
4894
4895
4897 if ( NULL == newband ) {
4898 elog(NOTICE, "Could not modify band for new raster. Returning new "
4899 "raster with the original band");
4900
4901 if (initexpr)
4902 pfree(initexpr);
4904 PG_FREE_IF_COPY(pgraster, 0);
4905
4906
4909 if (NULL == pgrtn) {
4910 elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
4911 PG_RETURN_NULL();
4912 }
4913
4914 SET_VARSIZE(pgrtn, pgrtn->
size);
4915 PG_RETURN_POINTER(pgrtn);
4916 }
4917
4919 width, height);
4920
4921 if (initexpr != NULL) {
4922
4924 pfree(initexpr); initexpr=newexpr;
4925
4926 sprintf(place,"$1");
4927 for (i = 0, j = 1; i < argkwcount; i++) {
4928 len = 0;
4930 pfree(initexpr); initexpr=newexpr;
4931 if (len > 0) {
4932 argtype[argcount] = argkwtypes[i];
4933 argcount++;
4934 argpos[i] = j++;
4935
4936 sprintf(place, "$%d", j);
4937 }
4938 else {
4939 argpos[i] = 0;
4940 }
4941 }
4942
4944
4945
4946 values = (Datum *) palloc(sizeof(Datum) * argcount);
4947 if (values == NULL) {
4948
4949 SPI_finish();
4950
4952 PG_FREE_IF_COPY(pgraster, 0);
4954
4955 elog(ERROR, "RASTER_mapAlgebraExpr: Could not allocate memory for value parameters of prepared statement");
4956 PG_RETURN_NULL();
4957 }
4958
4959
4960 nulls = (char *)palloc(argcount);
4961 if (nulls == NULL) {
4962
4963 SPI_finish();
4964
4966 PG_FREE_IF_COPY(pgraster, 0);
4968
4969 elog(ERROR, "RASTER_mapAlgebraExpr: Could not allocate memory for null parameters of prepared statement");
4970 PG_RETURN_NULL();
4971 }
4972
4973
4974 ret = SPI_connect();
4975 if (ret != SPI_OK_CONNECT) {
4976
4977 if (initexpr)
4978 pfree(initexpr);
4980 PG_FREE_IF_COPY(pgraster, 0);
4982
4983 elog(ERROR, "RASTER_mapAlgebraExpr: Could not connect to the SPI manager");
4984 PG_RETURN_NULL();
4985 };
4986
4987
4988 spi_plan = SPI_prepare(initexpr, argcount, argtype);
4989
4990 if (spi_plan == NULL) {
4991
4993 PG_FREE_IF_COPY(pgraster, 0);
4995
4996 SPI_finish();
4997
4998 pfree(initexpr);
4999
5000 elog(ERROR, "RASTER_mapAlgebraExpr: Could not prepare expression");
5001 PG_RETURN_NULL();
5002 }
5003 }
5004
5005 for (x = 0;
x < width;
x++) {
5006 for(y = 0;
y < height;
y++) {
5008
5014 if (skipcomputation == 0) {
5015 if (initexpr != NULL) {
5016
5017 memset(nulls, 'n', argcount);
5018
5019 for (i = 0; i < argkwcount; i++) {
5020 idx = argpos[i];
5021 if (idx < 1) continue;
5022 idx--;
5023
5024 if (i == kX ) {
5025
5026 values[idx] = Int32GetDatum(x+1);
5027 nulls[idx] = ' ';
5028 }
5029 else if (i == kY) {
5030
5031 values[idx] = Int32GetDatum(y+1);
5032 nulls[idx] = ' ';
5033 }
5034 else if (i == kVAL ) {
5035 values[idx] = Float8GetDatum(
r);
5036 nulls[idx] = ' ';
5037 }
5038
5039 }
5040
5041 ret = SPI_execute_plan(spi_plan, values, nulls,
FALSE, 0);
5042 if (ret != SPI_OK_SELECT || SPI_tuptable == NULL ||
5043 SPI_processed != 1) {
5044 if (SPI_tuptable)
5045 SPI_freetuptable(tuptable);
5046
5047 SPI_freeplan(spi_plan);
5048 SPI_finish();
5049
5050 pfree(values);
5051 pfree(nulls);
5052 pfree(initexpr);
5053
5055 PG_FREE_IF_COPY(pgraster, 0);
5057
5058 elog(ERROR, "RASTER_mapAlgebraExpr: Error executing prepared plan");
5059
5060 PG_RETURN_NULL();
5061 }
5062
5063 tupdesc = SPI_tuptable->tupdesc;
5064 tuptable = SPI_tuptable;
5065
5066 tuple = tuptable->vals[0];
5067 datum = SPI_getbinval(tuple, tupdesc, 1, &isnull);
5068 if ( SPI_result == SPI_ERROR_NOATTRIBUTE ) {
5069 POSTGIS_RT_DEBUGF(3,
"Expression for pixel %d,%d (value %g) errored, skip setting", x+1,y+1,
r);
5070 newval = newinitialvalue;
5071 }
5072 else if ( isnull ) {
5073 POSTGIS_RT_DEBUGF(3,
"Expression for pixel %d,%d (value %g) evaluated to NULL, skip setting", x+1,y+1,
r);
5074 newval = newinitialvalue;
5075 } else {
5076 newval = DatumGetFloat8(datum);
5077 }
5078
5079 SPI_freetuptable(tuptable);
5080 }
5081
5082 else
5083 newval = newinitialvalue;
5084
5086 newval);
5087 }
5088
5089
5091 }
5092
5093 }
5094 }
5095
5096 if (initexpr != NULL) {
5097 SPI_freeplan(spi_plan);
5098 SPI_finish();
5099
5100 pfree(values);
5101 pfree(nulls);
5102 pfree(initexpr);
5103 }
5104 else {
5106 }
5107
5108
5109
5110
5111 POSTGIS_RT_DEBUG(3,
"RASTER_mapAlgebraExpr: raster modified, serializing it.");
5112
5113
5115 PG_FREE_IF_COPY(pgraster, 0);
5116
5119 if (NULL == pgrtn)
5120 PG_RETURN_NULL();
5121
5122 SET_VARSIZE(pgrtn, pgrtn->
size);
5123
5125
5126
5128
5129
5130 PG_RETURN_POINTER(pgrtn);
5131}
#define RASTER_DEBUG(level, msg)
int32_t rt_raster_get_srid(rt_raster raster)
Get raster's SRID.
double rt_raster_get_x_skew(rt_raster raster)
Get skew about the X axis.
double rt_raster_get_x_offset(rt_raster raster)
Get raster x offset, in projection units.
int rt_raster_generate_new_band(rt_raster raster, rt_pixtype pixtype, double initialvalue, uint32_t hasnodata, double nodatavalue, int index)
Generate a new inline band and add it to a raster.
void rt_raster_set_scale(rt_raster raster, double scaleX, double scaleY)
Set scale in projection units.
int rt_band_get_hasnodata_flag(rt_band band)
Get hasnodata flag value.
rt_pixtype rt_pixtype_index_from_name(const char *pixname)
rt_errorstate rt_band_get_pixel(rt_band band, int x, int y, double *value, int *nodata)
Get pixel value.
void rt_raster_destroy(rt_raster raster)
Release memory associated to a raster.
void rt_raster_set_skews(rt_raster raster, double skewX, double skewY)
Set skews about the X and Y axis.
int rt_band_get_isnodata_flag(rt_band band)
Get isnodata flag value.
rt_raster rt_raster_new(uint32_t width, uint32_t height)
Construct a raster with given dimensions.
int rt_raster_has_band(rt_raster raster, int nband)
Return TRUE if the raster has a band of this number.
double rt_raster_get_x_scale(rt_raster raster)
Get scale X in projection units.
const char * rt_pixtype_name(rt_pixtype pixtype)
double rt_band_get_min_value(rt_band band)
Returns the minimal possible value for the band according to the pixel type.
rt_errorstate rt_band_set_pixel(rt_band band, int x, int y, double val, int *converted)
Set single pixel's value.
uint16_t rt_raster_get_num_bands(rt_raster raster)
uint16_t rt_raster_get_height(rt_raster raster)
void rt_raster_set_srid(rt_raster raster, int32_t srid)
Set raster's SRID.
rt_errorstate rt_band_get_nodata(rt_band band, double *nodata)
Get NODATA value.
rt_pixtype rt_band_get_pixtype(rt_band band)
Return pixeltype of this band.
uint16_t rt_raster_get_width(rt_raster raster)
void * rt_raster_serialize(rt_raster raster)
Return this raster in serialized form.
int rt_raster_copy_band(rt_raster torast, rt_raster fromrast, int fromindex, int toindex)
Copy one band from one raster to another.
double rt_raster_get_y_scale(rt_raster raster)
Get scale Y in projection units.
double rt_raster_get_y_skew(rt_raster raster)
Get skew about the Y axis.
void rt_raster_set_offsets(rt_raster raster, double x, double y)
Set insertion points in projection units.
rt_raster rt_raster_deserialize(void *serialized, int header_only)
Return a raster from a serialized form.
double rt_raster_get_y_offset(rt_raster raster)
Get raster y offset, in projection units.
int rt_raster_is_empty(rt_raster raster)
Return TRUE if the raster is empty.
rt_band rt_raster_get_band(rt_raster raster, int bandNum)
Return Nth band, or NULL if unavailable.
raster
Be careful!! Zeros function's input parameter can be a (height x width) array, not (width x height): ...
char * text_to_cstring(const text *textptr)
char * rtpg_strreplace(const char *str, const char *oldstr, const char *newstr, int *count)
#define POSTGIS_RT_DEBUG(level, msg)
#define POSTGIS_RT_DEBUGF(level, msg,...)