PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches

◆ RASTER_clip()

Datum RASTER_clip ( PG_FUNCTION_ARGS  )

Used to send to GDAL if ALL_TOUCHED rasterization is requested

How many options we have currently. Right now we'll have at most 1, but that could change in future

Definition at line 2973 of file rtpg_mapalgebra.c.

2974{
2975 rt_pgraster *pgraster = NULL;
2976 LWGEOM *rastgeom = NULL;
2977 double gt[6] = {0};
2978 int32_t srid = SRID_UNKNOWN;
2979
2980 rt_pgraster *pgrtn = NULL;
2981 rt_raster rtn = NULL;
2982
2983 GSERIALIZED *gser = NULL;
2984 LWGEOM *geom = NULL;
2985 lwvarlena_t *wkb = NULL;
2986
2987 ArrayType *array;
2988 Oid etype;
2989 Datum *e;
2990 bool *nulls;
2991
2992 int16 typlen;
2993 bool typbyval;
2994 char typalign;
2995
2996 int i = 0;
2997 int j = 0;
2998 int k = 0;
2999 rtpg_clip_arg arg = NULL;
3000 LWGEOM *tmpgeom = NULL;
3001
3002 rt_pixtype pixtype;
3003 int hasnodata;
3004 double nodataval;
3005
3006 double offset[4] = {0.};
3007 int input_x = 0;
3008 int input_y = 0;
3009 int mask_x = 0;
3010 int mask_y = 0;
3011 int x = 0;
3012 int y = 0;
3013 int width = 0;
3014 int height = 0;
3015 int mask_width = 0;
3016 int mask_height = 0;
3017 rt_band input_band = NULL;
3018 rt_band mask_band = NULL;
3019 rt_band output_band = NULL;
3020 double value;
3021 int isnodata;
3023 char **options = NULL;
3025 int options_len = 0;
3026
3027 POSTGIS_RT_DEBUG(3, "Starting...");
3028
3029 /* raster or geometry is NULL, return NULL */
3030 if (PG_ARGISNULL(0) || PG_ARGISNULL(2))
3031 PG_RETURN_NULL();
3032
3033 /* init arg */
3034 arg = rtpg_clip_arg_init();
3035 if (arg == NULL) {
3036 elog(ERROR, "RASTER_clip: Could not initialize argument structure");
3037 PG_RETURN_NULL();
3038 }
3039
3040 /* raster (0) */
3041 pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
3042
3043 /* Get raster object */
3044 arg->raster = rt_raster_deserialize(pgraster, FALSE);
3045 if (arg->raster == NULL) {
3047 PG_FREE_IF_COPY(pgraster, 0);
3048 elog(ERROR, "RASTER_clip: Could not deserialize raster");
3049 PG_RETURN_NULL();
3050 }
3051
3052 /* raster is empty, return empty raster */
3053 if (rt_raster_is_empty(arg->raster) || rt_raster_get_num_bands(arg->raster) == 0) {
3054 elog(NOTICE, "Input raster is empty or has no bands. Returning empty raster");
3055
3057 PG_FREE_IF_COPY(pgraster, 0);
3058
3059 rtn = rt_raster_new(0, 0);
3060 if (rtn == NULL) {
3061 elog(ERROR, "RASTER_clip: Could not create empty raster");
3062 PG_RETURN_NULL();
3063 }
3064
3065 pgrtn = rt_raster_serialize(rtn);
3066 rt_raster_destroy(rtn);
3067 if (NULL == pgrtn)
3068 PG_RETURN_NULL();
3069
3070 SET_VARSIZE(pgrtn, pgrtn->size);
3071 PG_RETURN_POINTER(pgrtn);
3072 }
3073
3074 /* metadata */
3076 srid = clamp_srid(rt_raster_get_srid(arg->raster));
3077
3078 /* geometry (2) */
3079 gser = PG_GETARG_GSERIALIZED_P(2);
3080 geom = lwgeom_from_gserialized(gser);
3081
3082 /* Get a 2D version of the geometry if necessary */
3083 if (lwgeom_ndims(geom) > 2) {
3084 LWGEOM *geom2d = lwgeom_force_2d(geom);
3085 lwgeom_free(geom);
3086 geom = geom2d;
3087 }
3088
3089 /* check that SRIDs match */
3090 if (srid != clamp_srid(gserialized_get_srid(gser))) {
3091 elog(NOTICE, "Geometry provided does not have the same SRID as the raster. Returning NULL");
3092
3094 PG_FREE_IF_COPY(pgraster, 0);
3095 lwgeom_free(geom);
3096 PG_FREE_IF_COPY(gser, 2);
3097
3098 PG_RETURN_NULL();
3099 }
3100
3101 /* crop (4) */
3102 if (!PG_ARGISNULL(4) && !PG_GETARG_BOOL(4))
3103 arg->extenttype = ET_FIRST;
3104
3105 /* get intersection geometry of input raster and input geometry */
3106 if (rt_raster_get_convex_hull(arg->raster, &rastgeom) != ES_NONE) {
3107
3109 PG_FREE_IF_COPY(pgraster, 0);
3110 lwgeom_free(geom);
3111 PG_FREE_IF_COPY(gser, 2);
3112
3113 elog(ERROR, "RASTER_clip: Could not get convex hull of raster");
3114 PG_RETURN_NULL();
3115 }
3116
3117 tmpgeom = lwgeom_intersection(rastgeom, geom);
3118 lwgeom_free(rastgeom);
3119 lwgeom_free(geom);
3120 PG_FREE_IF_COPY(gser, 2);
3121 geom = tmpgeom;
3122
3123 /* intersection is empty AND extent type is INTERSECTION, return empty */
3124 if (lwgeom_is_empty(geom) && arg->extenttype == ET_INTERSECTION) {
3125 elog(NOTICE, "The input raster and input geometry do not intersect. Returning empty raster");
3126
3128 PG_FREE_IF_COPY(pgraster, 0);
3129 lwgeom_free(geom);
3130
3131 rtn = rt_raster_new(0, 0);
3132 if (rtn == NULL) {
3133 elog(ERROR, "RASTER_clip: Could not create empty raster");
3134 PG_RETURN_NULL();
3135 }
3136
3137 pgrtn = rt_raster_serialize(rtn);
3138 rt_raster_destroy(rtn);
3139 if (NULL == pgrtn)
3140 PG_RETURN_NULL();
3141
3142 SET_VARSIZE(pgrtn, pgrtn->size);
3143 PG_RETURN_POINTER(pgrtn);
3144 }
3145
3146 /* nband (1) */
3147 if (!PG_ARGISNULL(1)) {
3148 array = PG_GETARG_ARRAYTYPE_P(1);
3149 etype = ARR_ELEMTYPE(array);
3150 get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
3151
3152 switch (etype) {
3153 case INT2OID:
3154 case INT4OID:
3155 break;
3156 default:
3158 PG_FREE_IF_COPY(pgraster, 0);
3159 lwgeom_free(geom);
3160 elog(ERROR, "RASTER_clip: Invalid data type for band indexes");
3161 PG_RETURN_NULL();
3162 break;
3163 }
3164
3165 deconstruct_array(
3166 array, etype,
3167 typlen, typbyval, typalign,
3168 &e, &nulls, &(arg->numbands)
3169 );
3170
3171 arg->band = palloc(sizeof(struct rtpg_clip_band_t) * arg->numbands);
3172 if (arg->band == NULL) {
3174 PG_FREE_IF_COPY(pgraster, 0);
3175 lwgeom_free(geom);
3176 elog(ERROR, "RASTER_clip: Could not allocate memory for band arguments");
3177 PG_RETURN_NULL();
3178 }
3179
3180 for (i = 0, j = 0; i < arg->numbands; i++) {
3181 if (nulls[i]) continue;
3182
3183 switch (etype) {
3184 case INT2OID:
3185 arg->band[j].nband = DatumGetInt16(e[i]) - 1;
3186 break;
3187 case INT4OID:
3188 arg->band[j].nband = DatumGetInt32(e[i]) - 1;
3189 break;
3190 }
3191
3192 j++;
3193 }
3194
3195 if (j < arg->numbands) {
3196 arg->band = repalloc(arg->band, sizeof(struct rtpg_clip_band_t) * j);
3197 if (arg->band == NULL) {
3199 PG_FREE_IF_COPY(pgraster, 0);
3200 lwgeom_free(geom);
3201 elog(ERROR, "RASTER_clip: Could not reallocate memory for band arguments");
3202 PG_RETURN_NULL();
3203 }
3204
3205 arg->numbands = j;
3206 }
3207
3208 /* validate band */
3209 for (i = 0; i < arg->numbands; i++) {
3210 if (!rt_raster_has_band(arg->raster, arg->band[i].nband)) {
3211 elog(NOTICE, "Band at index %d not found in raster", arg->band[i].nband + 1);
3213 PG_FREE_IF_COPY(pgraster, 0);
3214 lwgeom_free(geom);
3215 PG_RETURN_NULL();
3216 }
3217
3218 arg->band[i].hasnodata = 0;
3219 arg->band[i].nodataval = 0;
3220 }
3221 }
3222 else {
3224
3225 /* raster may have no bands */
3226 if (arg->numbands) {
3227 arg->band = palloc(sizeof(struct rtpg_clip_band_t) * arg->numbands);
3228 if (arg->band == NULL) {
3229
3231 PG_FREE_IF_COPY(pgraster, 0);
3232 lwgeom_free(geom);
3233
3234 elog(ERROR, "RASTER_clip: Could not allocate memory for band arguments");
3235 PG_RETURN_NULL();
3236 }
3237
3238 for (i = 0; i < arg->numbands; i++) {
3239 arg->band[i].nband = i;
3240 arg->band[i].hasnodata = 0;
3241 arg->band[i].nodataval = 0;
3242 }
3243 }
3244 }
3245
3246 /* nodataval (3) */
3247 if (!PG_ARGISNULL(3)) {
3248 array = PG_GETARG_ARRAYTYPE_P(3);
3249 etype = ARR_ELEMTYPE(array);
3250 get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
3251
3252 switch (etype) {
3253 case FLOAT4OID:
3254 case FLOAT8OID:
3255 break;
3256 default:
3258 PG_FREE_IF_COPY(pgraster, 0);
3259 lwgeom_free(geom);
3260 elog(ERROR, "RASTER_clip: Invalid data type for NODATA values");
3261 PG_RETURN_NULL();
3262 break;
3263 }
3264
3265 deconstruct_array(
3266 array, etype,
3267 typlen, typbyval, typalign,
3268 &e, &nulls, &k
3269 );
3270
3271 /* it doesn't matter if there are more nodataval */
3272 for (i = 0, j = 0; i < arg->numbands; i++, j++) {
3273 /* cap j to the last nodataval element */
3274 if (j >= k)
3275 j = k - 1;
3276
3277 if (nulls[j])
3278 continue;
3279
3280 arg->band[i].hasnodata = 1;
3281 switch (etype) {
3282 case FLOAT4OID:
3283 arg->band[i].nodataval = DatumGetFloat4(e[j]);
3284 break;
3285 case FLOAT8OID:
3286 arg->band[i].nodataval = DatumGetFloat8(e[j]);
3287 break;
3288 }
3289 }
3290 }
3291
3292 /* get wkb of geometry */
3293 POSTGIS_RT_DEBUG(3, "getting wkb of geometry");
3294 wkb = lwgeom_to_wkb_varlena(geom, WKB_SFSQL);
3295 lwgeom_free(geom);
3296
3297 /* we want to mark all pixels that are inside or touching the geometry - touched (5) */
3298 if (!PG_ARGISNULL(5) && PG_GETARG_BOOL(5) == TRUE){
3299 options_len = options_len + 1;
3300 options = (char **) palloc(sizeof(char *) * options_len);
3301 options[options_len - 1] = palloc(sizeof(char*) * (strlen("ALL_TOUCHED=TRUE") + 1));
3302 strcpy(options[options_len - 1], "ALL_TOUCHED=TRUE");
3303 }
3304
3305 if (options_len) {
3306 options_len++;
3307 options = (char **) repalloc(options, sizeof(char *) * options_len);
3308 options[options_len - 1] = NULL;
3309 }
3310
3311 /* rasterize geometry */
3312 arg->mask = rt_raster_gdal_rasterize((unsigned char *)wkb->data,
3313 LWSIZE_GET(wkb->size) - LWVARHDRSZ,
3314 NULL,
3315 0,
3316 NULL,
3317 NULL,
3318 NULL,
3319 NULL,
3320 NULL,
3321 NULL,
3322 NULL,
3323 &(gt[1]),
3324 &(gt[5]),
3325 NULL,
3326 NULL,
3327 &(gt[0]),
3328 &(gt[3]),
3329 &(gt[2]),
3330 &(gt[4]),
3331 options);
3332
3333 pfree(wkb);
3334
3335 if (options_len) pfree(options);
3336
3337 if (arg->mask == NULL) {
3339 PG_FREE_IF_COPY(pgraster, 0);
3340 elog(ERROR, "RASTER_clip: Could not rasterize intersection geometry");
3341 PG_RETURN_NULL();
3342 }
3343
3344 /* set SRID */
3345 rt_raster_set_srid(arg->mask, srid);
3346
3347 mask_width = rt_raster_get_width(arg->mask);
3348 mask_height = rt_raster_get_height(arg->mask);
3349
3350 if (rt_raster_from_two_rasters(arg->raster, arg->mask, arg->extenttype, &rtn, offset) != ES_NONE) {
3352 PG_FREE_IF_COPY(pgraster, 0);
3353 elog(ERROR, "RASTER_clip: Could not compute extent of rasters");
3354 PG_RETURN_NULL();
3355 }
3356
3357 width = rt_raster_get_width(rtn);
3358 height = rt_raster_get_height(rtn);
3359
3360 mask_band = rt_raster_get_band(arg->mask, 0);
3361
3362 for (i = 0; i < arg->numbands; i++) {
3363 input_band = rt_raster_get_band(arg->raster, arg->band[i].nband);
3364
3365 /* band metadata */
3366 pixtype = rt_band_get_pixtype(input_band);
3367
3368 if (arg->band[i].hasnodata) {
3369 hasnodata = 1;
3370 nodataval = arg->band[i].nodataval;
3371 }
3372 else if (rt_band_get_hasnodata_flag(input_band)) {
3373 hasnodata = 1;
3374 rt_band_get_nodata(input_band, &nodataval);
3375 }
3376 else {
3377 hasnodata = 0;
3378 nodataval = rt_band_get_min_value(input_band);
3379 }
3380
3381 if (rt_raster_generate_new_band(rtn, pixtype, nodataval, hasnodata, nodataval, i) < 0) {
3383 PG_FREE_IF_COPY(pgraster, 0);
3384 elog(ERROR, "RASTER_clip: Could not add new band in output raster");
3385 PG_RETURN_NULL();
3386 }
3387
3388 if (rt_band_get_isnodata_flag(input_band)) {
3389 continue;
3390 }
3391
3392 output_band = rt_raster_get_band(rtn, arg->band[i].nband);
3393
3394 if (!mask_band) {
3395 continue;
3396 }
3397
3398 for (y = 0; y < height; y++) {
3399 for (x = 0; x < width; x++) {
3400 mask_x = x - (int)offset[2];
3401 mask_y = y - (int)offset[3];
3402
3403 if (!(
3404 mask_x >= 0 &&
3405 mask_x < mask_width &&
3406 mask_y >= 0 &&
3407 mask_y < mask_height
3408 )) {
3409 continue;
3410 }
3411
3412 if (rt_band_get_pixel(mask_band, mask_x, mask_y, &value, &isnodata) != ES_NONE) {
3414 PG_FREE_IF_COPY(pgraster, 0);
3415 elog(ERROR, "RASTER_clip: Could not get pixel value");
3416 PG_RETURN_NULL();
3417 }
3418
3419 if (isnodata) {
3420 continue;
3421 }
3422
3423 input_x = x - (int)offset[0];
3424 input_y = y - (int)offset[1];
3425
3426 if (rt_band_get_pixel(input_band, input_x, input_y, &value, &isnodata) != ES_NONE) {
3428 PG_FREE_IF_COPY(pgraster, 0);
3429 elog(ERROR, "RASTER_clip: Could not get pixel value");
3430 PG_RETURN_NULL();
3431 }
3432
3433 if (isnodata) {
3434 continue;
3435 }
3436
3437 if (rt_band_set_pixel(output_band, x, y, value, NULL)) {
3439 PG_FREE_IF_COPY(pgraster, 0);
3440 elog(ERROR, "RASTER_clip: Could not set pixel value");
3441 PG_RETURN_NULL();
3442 }
3443 }
3444 }
3445 }
3446
3448 PG_FREE_IF_COPY(pgraster, 0);
3449
3450 pgrtn = rt_raster_serialize(rtn);
3451 rt_raster_destroy(rtn);
3452
3453 POSTGIS_RT_DEBUG(3, "Finished");
3454
3455 if (!pgrtn)
3456 PG_RETURN_NULL();
3457
3458 SET_VARSIZE(pgrtn, pgrtn->size);
3459 PG_RETURN_POINTER(pgrtn);
3460}
#define TRUE
Definition dbfopen.c:73
#define FALSE
Definition dbfopen.c:72
int32_t gserialized_get_srid(const GSERIALIZED *g)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
int lwgeom_ndims(const LWGEOM *geom)
Return the number of dimensions (2, 3, 4) in a geometry.
Definition lwgeom.c:983
#define LWVARHDRSZ
Definition liblwgeom.h:311
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1246
#define LWSIZE_GET(varsize)
Macro for reading the size from the GSERIALIZED size attribute.
Definition liblwgeom.h:324
lwvarlena_t * lwgeom_to_wkb_varlena(const LWGEOM *geom, uint8_t variant)
Definition lwout_wkb.c:851
LWGEOM * lwgeom_force_2d(const LWGEOM *geom)
Strip out the Z/M components of an LWGEOM.
Definition lwgeom.c:821
LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2)
#define SRID_UNKNOWN
Unknown SRID value.
Definition liblwgeom.h:215
#define WKB_SFSQL
Definition liblwgeom.h:2211
int32_t clamp_srid(int32_t srid)
Return a valid SRID from an arbitrary integer Raises a notice if what comes out is different from wha...
Definition lwutil.c:339
int32_t rt_raster_get_srid(rt_raster raster)
Get raster's SRID.
Definition rt_raster.c:360
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.
Definition rt_raster.c:489
int rt_band_get_hasnodata_flag(rt_band band)
Get hasnodata flag value.
Definition rt_band.c:833
rt_errorstate rt_band_get_pixel(rt_band band, int x, int y, double *value, int *nodata)
Get pixel value.
Definition rt_band.c:1551
void rt_raster_destroy(rt_raster raster)
Release memory associated to a raster.
Definition rt_raster.c:86
rt_pixtype
Definition librtcore.h:188
int rt_band_get_isnodata_flag(rt_band band)
Get isnodata flag value.
Definition rt_band.c:873
rt_raster rt_raster_new(uint32_t width, uint32_t height)
Construct a raster with given dimensions.
Definition rt_raster.c:52
int rt_raster_has_band(rt_raster raster, int nband)
Return TRUE if the raster has a band of this number.
Definition rt_raster.c:1253
double rt_band_get_min_value(rt_band band)
Returns the minimal possible value for the band according to the pixel type.
Definition rt_band.c:2082
rt_errorstate rt_band_set_pixel(rt_band band, int x, int y, double val, int *converted)
Set single pixel's value.
Definition rt_band.c:1140
@ ES_NONE
Definition librtcore.h:182
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)
Return a raster of the provided geometry.
Definition rt_raster.c:2502
uint16_t rt_raster_get_num_bands(rt_raster raster)
Definition rt_raster.c:376
rt_errorstate rt_raster_get_convex_hull(rt_raster raster, LWGEOM **hull)
Get raster's convex hull.
uint16_t rt_raster_get_height(rt_raster raster)
Definition rt_raster.c:133
void rt_raster_set_srid(rt_raster raster, int32_t srid)
Set raster's SRID.
Definition rt_raster.c:367
@ ET_INTERSECTION
Definition librtcore.h:205
@ ET_FIRST
Definition librtcore.h:207
rt_errorstate rt_band_get_nodata(rt_band band, double *nodata)
Get NODATA value.
Definition rt_band.c:2067
rt_pixtype rt_band_get_pixtype(rt_band band)
Return pixeltype of this band.
Definition rt_band.c:790
uint16_t rt_raster_get_width(rt_raster raster)
Definition rt_raster.c:125
void * rt_raster_serialize(rt_raster raster)
Return this raster in serialized form.
rt_errorstate rt_raster_from_two_rasters(rt_raster rast1, rt_raster rast2, rt_extenttype extenttype, rt_raster *rtnraster, double *offset)
Definition rt_raster.c:3348
void rt_raster_get_geotransform_matrix(rt_raster raster, double *gt)
Get 6-element array of raster geotransform matrix.
Definition rt_raster.c:588
rt_raster rt_raster_deserialize(void *serialized, int header_only)
Return a raster from a serialized form.
int rt_raster_is_empty(rt_raster raster)
Return TRUE if the raster is empty.
Definition rt_raster.c:1240
rt_band rt_raster_get_band(rt_raster raster, int bandNum)
Return Nth band, or NULL if unavailable.
Definition rt_raster.c:385
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition lwinline.h:199
int value
Definition genraster.py:62
static rtpg_clip_arg rtpg_clip_arg_init(void)
static void rtpg_clip_arg_destroy(rtpg_clip_arg arg)
#define POSTGIS_RT_DEBUG(level, msg)
Definition rtpostgis.h:65
uint32_t size
Definition liblwgeom.h:307
char data[]
Definition liblwgeom.h:308
Struct definitions.
Definition librtcore.h:2452
rtpg_clip_band band
rt_extenttype extenttype

References rtpg_clip_arg_t::band, clamp_srid(), lwvarlena_t::data, ES_NONE, ET_FIRST, ET_INTERSECTION, rtpg_clip_arg_t::extenttype, FALSE, gserialized_get_srid(), rtpg_clip_band_t::hasnodata, lwgeom_force_2d(), lwgeom_free(), lwgeom_from_gserialized(), lwgeom_intersection(), lwgeom_is_empty(), lwgeom_ndims(), lwgeom_to_wkb_varlena(), LWSIZE_GET, LWVARHDRSZ, rtpg_clip_arg_t::mask, rtpg_clip_band_t::nband, rtpg_clip_band_t::nodataval, rtpg_clip_arg_t::numbands, POSTGIS_RT_DEBUG, rtpg_clip_arg_t::raster, rt_band_get_hasnodata_flag(), rt_band_get_isnodata_flag(), rt_band_get_min_value(), rt_band_get_nodata(), rt_band_get_pixel(), rt_band_get_pixtype(), rt_band_set_pixel(), rt_raster_deserialize(), rt_raster_destroy(), rt_raster_from_two_rasters(), rt_raster_gdal_rasterize(), rt_raster_generate_new_band(), rt_raster_get_band(), rt_raster_get_convex_hull(), rt_raster_get_geotransform_matrix(), rt_raster_get_height(), rt_raster_get_num_bands(), rt_raster_get_srid(), rt_raster_get_width(), rt_raster_has_band(), rt_raster_is_empty(), rt_raster_new(), rt_raster_serialize(), rt_raster_set_srid(), rtpg_clip_arg_destroy(), rtpg_clip_arg_init(), lwvarlena_t::size, rt_raster_serialized_t::size, SRID_UNKNOWN, TRUE, and WKB_SFSQL.

Here is the call graph for this function: