PostGIS  2.2.7dev-r@@SVN_REVISION@@
static void compute_gserialized_stats_mode ( VacAttrStats *  stats,
AnalyzeAttrFetchFunc  fetchfunc,
int  sample_rows,
double  total_rows,
int  mode 
)
static

The gserialized_analyze_nd sets this function as a callback on the stats object when called by the ANALYZE command.

ANALYZE then gathers the requisite number of sample rows and then calls this function.

We could also pass stats->extra_data in from gserialized_analyze_nd (things like the column type or other stuff from the system catalogs) but so far we don't use that capability.

Our job is to build some statistics on the sample data for use by operator estimators.

We will populate an n-d histogram using the provided sample rows. The selectivity estimators (sel and j_oinsel) can then use the histogram

Definition at line 1235 of file gserialized_estimate.c.

References ND_STATS_T::cells_covered, ND_STATS_T::extent, gbox_is_valid(), gbox_ndims(), gserialized_get_gbox_p(), ND_STATS_T::histogram_cells, ND_STATS_T::histogram_features, LW_FAILURE, ND_BOX_T::max, ND_IBOX_T::max, ND_BOX_T::min, ND_IBOX_T::min, GBOX::mmax, GBOX::mmin, nd_box_array_distribution(), nd_box_expand(), nd_box_from_gbox(), nd_box_init(), nd_box_init_bounds(), nd_box_intersects(), nd_box_merge(), nd_box_overlap(), nd_box_ratio(), nd_box_to_json(), ND_DIMS, nd_increment(), nd_stats_to_json(), nd_stats_value_index(), ND_STATS_T::ndims, ND_STATS_T::not_null_features, ND_STATS_T::sample_features, SDFACTOR, ND_STATS_T::size, STATISTIC_KIND_2D, STATISTIC_KIND_ND, STATISTIC_SLOT_2D, STATISTIC_SLOT_ND, ND_STATS_T::table_features, total_double(), ND_STATS_T::value, GBOX::zmax, and GBOX::zmin.

Referenced by compute_gserialized_stats().

1237 {
1238  MemoryContext old_context;
1239  int d, i; /* Counters */
1240  int notnull_cnt = 0; /* # not null rows in the sample */
1241  int null_cnt = 0; /* # null rows in the sample */
1242  int histogram_features = 0; /* # rows that actually got counted in the histogram */
1243 
1244  ND_STATS *nd_stats; /* Our histogram */
1245  size_t nd_stats_size; /* Size to allocate */
1246 
1247  double total_width = 0; /* # of bytes used by sample */
1248  double total_sample_volume = 0; /* Area/volume coverage of the sample */
1249  double total_cell_count = 0; /* # of cells in histogram affected by sample */
1250 
1251  ND_BOX sum; /* Sum of extents of sample boxes */
1252  ND_BOX avg; /* Avg of extents of sample boxes */
1253  ND_BOX stddev; /* StdDev of extents of sample boxes */
1254 
1255  const ND_BOX **sample_boxes; /* ND_BOXes for each of the sample features */
1256  ND_BOX sample_extent; /* Extent of the raw sample */
1257  int histo_size[ND_DIMS]; /* histogram nrows, ncols, etc */
1258  ND_BOX histo_extent; /* Spatial extent of the histogram */
1259  ND_BOX histo_extent_new; /* Temporary variable */
1260  int histo_cells_target; /* Number of cells we will shoot for, given the stats target */
1261  int histo_cells; /* Number of cells in the histogram */
1262  int histo_cells_new = 1; /* Temporary variable */
1263 
1264  int ndims = 2; /* Dimensionality of the sample */
1265  int histo_ndims = 0; /* Dimensionality of the histogram */
1266  double sample_distribution[ND_DIMS]; /* How homogeneous is distribution of sample in each axis? */
1267  double total_distribution; /* Total of sample_distribution */
1268 
1269  int stats_slot; /* What slot is this data going into? (2D vs ND) */
1270  int stats_kind; /* And this is what? (2D vs ND) */
1271 
1272  /* Initialize sum and stddev */
1273  nd_box_init(&sum);
1274  nd_box_init(&stddev);
1275 
1276  /*
1277  * This is where gserialized_analyze_nd
1278  * should put its' custom parameters.
1279  */
1280  /* void *mystats = stats->extra_data; */
1281 
1282  POSTGIS_DEBUG(2, "compute_gserialized_stats called");
1283  POSTGIS_DEBUGF(3, " # sample_rows: %d", sample_rows);
1284  POSTGIS_DEBUGF(3, " estimate of total_rows: %.6g", total_rows);
1285 
1286  /*
1287  * We might need less space, but don't think
1288  * its worth saving...
1289  */
1290  sample_boxes = palloc(sizeof(ND_BOX*) * sample_rows);
1291 
1292  /*
1293  * First scan:
1294  * o read boxes
1295  * o find dimensionality of the sample
1296  * o find extent of the sample
1297  * o count null-infinite/not-null values
1298  * o compute total_width
1299  * o compute total features's box area (for avgFeatureArea)
1300  * o sum features box coordinates (for standard deviation)
1301  */
1302  for ( i = 0; i < sample_rows; i++ )
1303  {
1304  Datum datum;
1305  GSERIALIZED *geom;
1306  GBOX gbox;
1307  ND_BOX *nd_box;
1308  bool is_null;
1309  bool is_copy;
1310 
1311  datum = fetchfunc(stats, i, &is_null);
1312 
1313  /* Skip all NULLs. */
1314  if ( is_null )
1315  {
1316  POSTGIS_DEBUGF(4, " skipped null geometry %d", i);
1317  null_cnt++;
1318  continue;
1319  }
1320 
1321  /* Read the bounds from the gserialized. */
1322  geom = (GSERIALIZED *)PG_DETOAST_DATUM(datum);
1323  is_copy = VARATT_IS_EXTENDED(datum);
1324  if ( LW_FAILURE == gserialized_get_gbox_p(geom, &gbox) )
1325  {
1326  /* Skip empties too. */
1327  POSTGIS_DEBUGF(3, " skipped empty geometry %d", i);
1328  continue;
1329  }
1330 
1331  /* If we're in 2D mode, zero out the higher dimensions for "safety" */
1332  if ( mode == 2 )
1333  gbox.zmin = gbox.zmax = gbox.mmin = gbox.mmax = 0.0;
1334 
1335  /* Check bounds for validity (finite and not NaN) */
1336  if ( ! gbox_is_valid(&gbox) )
1337  {
1338  POSTGIS_DEBUGF(3, " skipped infinite/nan geometry %d", i);
1339  continue;
1340  }
1341 
1342  /*
1343  * In N-D mode, set the ndims to the maximum dimensionality found
1344  * in the sample. Otherwise, leave at ndims == 2.
1345  */
1346  if ( mode != 2 )
1347  ndims = Max(gbox_ndims(&gbox), ndims);
1348 
1349  /* Convert gbox to n-d box */
1350  nd_box = palloc(sizeof(ND_BOX));
1351  nd_box_from_gbox(&gbox, nd_box);
1352 
1353  /* Cache n-d bounding box */
1354  sample_boxes[notnull_cnt] = nd_box;
1355 
1356  /* Initialize sample extent before merging first entry */
1357  if ( ! notnull_cnt )
1358  nd_box_init_bounds(&sample_extent);
1359 
1360  /* Add current sample to overall sample extent */
1361  nd_box_merge(nd_box, &sample_extent);
1362 
1363  /* How many bytes does this sample use? */
1364  total_width += VARSIZE(geom);
1365 
1366  /* Add bounds coordinates to sums for stddev calculation */
1367  for ( d = 0; d < ndims; d++ )
1368  {
1369  sum.min[d] += nd_box->min[d];
1370  sum.max[d] += nd_box->max[d];
1371  }
1372 
1373  /* Increment our "good feature" count */
1374  notnull_cnt++;
1375 
1376  /* Free up memory if our sample geometry was copied */
1377  if ( is_copy )
1378  pfree(geom);
1379 
1380  /* Give backend a chance of interrupting us */
1381  vacuum_delay_point();
1382  }
1383 
1384  /*
1385  * We'll build a histogram having stats->attr->attstattarget cells
1386  * on each side, within reason... we'll use ndims*10000 as the
1387  * maximum number of cells.
1388  * Also, if we're sampling a relatively small table, we'll try to ensure that
1389  * we have an average of 5 features for each cell so the histogram isn't
1390  * so sparse.
1391  */
1392  histo_cells_target = (int)pow((double)(stats->attr->attstattarget), (double)ndims);
1393  histo_cells_target = Min(histo_cells_target, ndims * 10000);
1394  histo_cells_target = Min(histo_cells_target, (int)(total_rows/5));
1395  POSTGIS_DEBUGF(3, " stats->attr->attstattarget: %d", stats->attr->attstattarget);
1396  POSTGIS_DEBUGF(3, " target # of histogram cells: %d", histo_cells_target);
1397 
1398  /* If there's no useful features, we can't work out stats */
1399  if ( ! notnull_cnt )
1400  {
1401  elog(NOTICE, "no non-null/empty features, unable to compute statistics");
1402  stats->stats_valid = false;
1403  return;
1404  }
1405 
1406  POSTGIS_DEBUGF(3, " sample_extent: %s", nd_box_to_json(&sample_extent, ndims));
1407 
1408  /*
1409  * Second scan:
1410  * o compute standard deviation
1411  */
1412  for ( d = 0; d < ndims; d++ )
1413  {
1414  /* Calculate average bounds values */
1415  avg.min[d] = sum.min[d] / notnull_cnt;
1416  avg.max[d] = sum.max[d] / notnull_cnt;
1417 
1418  /* Calculate standard deviation for this dimension bounds */
1419  for ( i = 0; i < notnull_cnt; i++ )
1420  {
1421  const ND_BOX *ndb = sample_boxes[i];
1422  stddev.min[d] += (ndb->min[d] - avg.min[d]) * (ndb->min[d] - avg.min[d]);
1423  stddev.max[d] += (ndb->max[d] - avg.max[d]) * (ndb->max[d] - avg.max[d]);
1424  }
1425  stddev.min[d] = sqrt(stddev.min[d] / notnull_cnt);
1426  stddev.max[d] = sqrt(stddev.max[d] / notnull_cnt);
1427 
1428  /* Histogram bounds for this dimension bounds is avg +/- SDFACTOR * stdev */
1429  histo_extent.min[d] = Max(avg.min[d] - SDFACTOR * stddev.min[d], sample_extent.min[d]);
1430  histo_extent.max[d] = Min(avg.max[d] + SDFACTOR * stddev.max[d], sample_extent.max[d]);
1431  }
1432 
1433  /*
1434  * Third scan:
1435  * o skip hard deviants
1436  * o compute new histogram box
1437  */
1438  nd_box_init_bounds(&histo_extent_new);
1439  for ( i = 0; i < notnull_cnt; i++ )
1440  {
1441  const ND_BOX *ndb = sample_boxes[i];
1442  /* Skip any hard deviants (boxes entirely outside our histo_extent */
1443  if ( ! nd_box_intersects(&histo_extent, ndb, ndims) )
1444  {
1445  POSTGIS_DEBUGF(4, " feature %d is a hard deviant, skipped", i);
1446  sample_boxes[i] = NULL;
1447  continue;
1448  }
1449  /* Expand our new box to fit all the other features. */
1450  nd_box_merge(ndb, &histo_extent_new);
1451  }
1452  /*
1453  * Expand the box slightly (1%) to avoid edge effects
1454  * with objects that are on the boundary
1455  */
1456  nd_box_expand(&histo_extent_new, 0.01);
1457  histo_extent = histo_extent_new;
1458 
1459  /*
1460  * How should we allocate our histogram cells to the
1461  * different dimensions? We can't do it by raw dimensional width,
1462  * because in x/y/z space, the z can have different units
1463  * from the x/y. Similarly for x/y/t space.
1464  * So, we instead calculate how much features overlap
1465  * each other in their dimension to figure out which
1466  * dimensions have useful selectivity characteristics (more
1467  * variability in density) and therefor would find
1468  * more cells useful (to distinguish between dense places and
1469  * homogeneous places).
1470  */
1471  nd_box_array_distribution(sample_boxes, notnull_cnt, &histo_extent, ndims,
1472  sample_distribution);
1473 
1474  /*
1475  * The sample_distribution array now tells us how spread out the
1476  * data is in each dimension, so we use that data to allocate
1477  * the histogram cells we have available.
1478  * At this point, histo_cells_target is the approximate target number
1479  * of cells.
1480  */
1481 
1482  /*
1483  * Some dimensions have basically a uniform distribution, we want
1484  * to allocate no cells to those dimensions, only to dimensions
1485  * that have some interesting differences in data distribution.
1486  * Here we count up the number of interesting dimensions
1487  */
1488  for ( d = 0; d < ndims; d++ )
1489  {
1490  if ( sample_distribution[d] > 0 )
1491  histo_ndims++;
1492  }
1493 
1494  if ( histo_ndims == 0 )
1495  {
1496  /* Special case: all our dimensions had low variability! */
1497  /* We just divide the cells up evenly */
1498  POSTGIS_DEBUG(3, " special case: no axes have variability");
1499  histo_cells_new = 1;
1500  for ( d = 0; d < ndims; d++ )
1501  {
1502  histo_size[d] = 1 + (int)pow((double)histo_cells_target, 1/(double)ndims);
1503  POSTGIS_DEBUGF(3, " histo_size[d]: %d", histo_size[d]);
1504  histo_cells_new *= histo_size[d];
1505  }
1506  POSTGIS_DEBUGF(3, " histo_cells_new: %d", histo_cells_new);
1507  }
1508  else
1509  {
1510  /*
1511  * We're going to express the amount of variability in each dimension
1512  * as a proportion of the total variability and allocate cells in that
1513  * dimension relative to that proportion.
1514  */
1515  POSTGIS_DEBUG(3, " allocating histogram axes based on axis variability");
1516  total_distribution = total_double(sample_distribution, ndims); /* First get the total */
1517  POSTGIS_DEBUGF(3, " total_distribution: %.8g", total_distribution);
1518  histo_cells_new = 1; /* For the number of cells in the final histogram */
1519  for ( d = 0; d < ndims; d++ )
1520  {
1521  if ( sample_distribution[d] == 0 ) /* Uninteresting dimensions don't get any room */
1522  {
1523  histo_size[d] = 1;
1524  }
1525  else /* Interesting dimension */
1526  {
1527  /* How does this dims variability compare to the total? */
1528  float edge_ratio = (float)sample_distribution[d] / (float)total_distribution;
1529  /*
1530  * Scale the target cells number by the # of dims and ratio,
1531  * then take the appropriate root to get the estimated number of cells
1532  * on this axis (eg, pow(0.5) for 2d, pow(0.333) for 3d, pow(0.25) for 4d)
1533  */
1534  histo_size[d] = (int)pow(histo_cells_target * histo_ndims * edge_ratio, 1/(double)histo_ndims);
1535  /* If something goes awry, just give this dim one slot */
1536  if ( ! histo_size[d] )
1537  histo_size[d] = 1;
1538  }
1539  histo_cells_new *= histo_size[d];
1540  }
1541  POSTGIS_DEBUGF(3, " histo_cells_new: %d", histo_cells_new);
1542  }
1543 
1544  /* Update histo_cells to the actual number of cells we need to allocate */
1545  histo_cells = histo_cells_new;
1546  POSTGIS_DEBUGF(3, " histo_cells: %d", histo_cells);
1547 
1548  /*
1549  * Create the histogram (ND_STATS) in the stats memory context
1550  */
1551  old_context = MemoryContextSwitchTo(stats->anl_context);
1552  nd_stats_size = sizeof(ND_STATS) + ((histo_cells - 1) * sizeof(float4));
1553  nd_stats = palloc(nd_stats_size);
1554  memset(nd_stats, 0, nd_stats_size); /* Initialize all values to 0 */
1555  MemoryContextSwitchTo(old_context);
1556 
1557  /* Initialize the #ND_STATS objects */
1558  nd_stats->ndims = ndims;
1559  nd_stats->extent = histo_extent;
1560  nd_stats->sample_features = sample_rows;
1561  nd_stats->table_features = total_rows;
1562  nd_stats->not_null_features = notnull_cnt;
1563  /* Copy in the histogram dimensions */
1564  for ( d = 0; d < ndims; d++ )
1565  nd_stats->size[d] = histo_size[d];
1566 
1567  /*
1568  * Fourth scan:
1569  * o fill histogram values with the proportion of
1570  * features' bbox overlaps: a feature's bvol
1571  * can fully overlap (1) or partially overlap
1572  * (fraction of 1) an histogram cell.
1573  *
1574  * Note that we are filling each cell with the "portion of
1575  * the feature's box that overlaps the cell". So, if we sum
1576  * up the values in the histogram, we could get the
1577  * histogram feature count.
1578  *
1579  */
1580  for ( i = 0; i < notnull_cnt; i++ )
1581  {
1582  const ND_BOX *nd_box;
1583  ND_IBOX nd_ibox;
1584  int at[ND_DIMS];
1585  int d;
1586  double num_cells = 0;
1587  double tmp_volume = 1.0;
1588  double min[ND_DIMS];
1589  double max[ND_DIMS];
1590  double cellsize[ND_DIMS];
1591 
1592  nd_box = sample_boxes[i];
1593  if ( ! nd_box ) continue; /* Skip Null'ed out hard deviants */
1594 
1595  /* Give backend a chance of interrupting us */
1596  vacuum_delay_point();
1597 
1598  /* Find the cells that overlap with this box and put them into the ND_IBOX */
1599  nd_box_overlap(nd_stats, nd_box, &nd_ibox);
1600  memset(at, 0, sizeof(int)*ND_DIMS);
1601 
1602  POSTGIS_DEBUGF(3, " feature %d: ibox (%d, %d, %d, %d) (%d, %d, %d, %d)", i,
1603  nd_ibox.min[0], nd_ibox.min[1], nd_ibox.min[2], nd_ibox.min[3],
1604  nd_ibox.max[0], nd_ibox.max[1], nd_ibox.max[2], nd_ibox.max[3]);
1605 
1606  for ( d = 0; d < nd_stats->ndims; d++ )
1607  {
1608  /* Initialize the starting values */
1609  at[d] = nd_ibox.min[d];
1610  min[d] = nd_stats->extent.min[d];
1611  max[d] = nd_stats->extent.max[d];
1612  cellsize[d] = (max[d] - min[d])/(nd_stats->size[d]);
1613 
1614  /* What's the volume (area) of this feature's box? */
1615  tmp_volume *= (nd_box->max[d] - nd_box->min[d]);
1616  }
1617 
1618  /* Add feature volume (area) to our total */
1619  total_sample_volume += tmp_volume;
1620 
1621  /*
1622  * Move through all the overlaped histogram cells values and
1623  * add the box overlap proportion to them.
1624  */
1625  do
1626  {
1627  ND_BOX nd_cell;
1628  double ratio;
1629  /* Create a box for this histogram cell */
1630  for ( d = 0; d < nd_stats->ndims; d++ )
1631  {
1632  nd_cell.min[d] = min[d] + (at[d]+0) * cellsize[d];
1633  nd_cell.max[d] = min[d] + (at[d]+1) * cellsize[d];
1634  }
1635 
1636  /*
1637  * If a feature box is completely inside one cell the ratio will be
1638  * 1.0. If a feature box is 50% in two cells, each cell will get
1639  * 0.5 added on.
1640  */
1641  ratio = nd_box_ratio(&nd_cell, nd_box, nd_stats->ndims);
1642  nd_stats->value[nd_stats_value_index(nd_stats, at)] += ratio;
1643  num_cells += ratio;
1644  POSTGIS_DEBUGF(3, " ratio (%.8g) num_cells (%.8g)", ratio, num_cells);
1645  POSTGIS_DEBUGF(3, " at (%d, %d, %d, %d)", at[0], at[1], at[2], at[3]);
1646  }
1647  while ( nd_increment(&nd_ibox, nd_stats->ndims, at) );
1648 
1649  /* Keep track of overall number of overlaps counted */
1650  total_cell_count += num_cells;
1651  /* How many features have we added to this histogram? */
1652  histogram_features++;
1653  }
1654 
1655  POSTGIS_DEBUGF(3, " histogram_features: %d", histogram_features);
1656  POSTGIS_DEBUGF(3, " sample_rows: %d", sample_rows);
1657  POSTGIS_DEBUGF(3, " table_rows: %.6g", total_rows);
1658 
1659  /* Error out if we got no sample information */
1660  if ( ! histogram_features )
1661  {
1662  POSTGIS_DEBUG(3, " no stats have been gathered");
1663  elog(NOTICE, " no features lie in the stats histogram, invalid stats");
1664  stats->stats_valid = false;
1665  return;
1666  }
1667 
1668  nd_stats->histogram_features = histogram_features;
1669  nd_stats->histogram_cells = histo_cells;
1670  nd_stats->cells_covered = total_cell_count;
1671 
1672  /* Put this histogram data into the right slot/kind */
1673  if ( mode == 2 )
1674  {
1675  stats_slot = STATISTIC_SLOT_2D;
1676  stats_kind = STATISTIC_KIND_2D;
1677  }
1678  else
1679  {
1680  stats_slot = STATISTIC_SLOT_ND;
1681  stats_kind = STATISTIC_KIND_ND;
1682  }
1683 
1684  /* Write the statistics data */
1685  stats->stakind[stats_slot] = stats_kind;
1686  stats->staop[stats_slot] = InvalidOid;
1687  stats->stanumbers[stats_slot] = (float4*)nd_stats;
1688  stats->numnumbers[stats_slot] = nd_stats_size/sizeof(float4);
1689  stats->stanullfrac = (float4)null_cnt/sample_rows;
1690  stats->stawidth = total_width/notnull_cnt;
1691  stats->stadistinct = -1.0;
1692  stats->stats_valid = true;
1693 
1694  POSTGIS_DEBUGF(3, " out: slot 0: kind %d (STATISTIC_KIND_ND)", stats->stakind[0]);
1695  POSTGIS_DEBUGF(3, " out: slot 0: op %d (InvalidOid)", stats->staop[0]);
1696  POSTGIS_DEBUGF(3, " out: slot 0: numnumbers %d", stats->numnumbers[0]);
1697  POSTGIS_DEBUGF(3, " out: null fraction: %f=%d/%d", stats->stanullfrac, null_cnt, sample_rows);
1698  POSTGIS_DEBUGF(3, " out: average width: %d bytes", stats->stawidth);
1699  POSTGIS_DEBUG (3, " out: distinct values: all (no check done)");
1700  POSTGIS_DEBUGF(3, " out: %s", nd_stats_to_json(nd_stats));
1701  /*
1702  POSTGIS_DEBUGF(3, " out histogram:\n%s", nd_stats_to_grid(nd_stats));
1703  */
1704 
1705  return;
1706 }
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *box)
Read the bounding box off a serialization and calculate one if it is not already there.
Definition: g_serialized.c:371
static int nd_increment(ND_IBOX *ibox, int ndims, int *counter)
Given an n-d index array (counter), and a domain to increment it in (ibox) increment it by one...
static int nd_box_overlap(const ND_STATS *nd_stats, const ND_BOX *nd_box, ND_IBOX *nd_ibox)
What stats cells overlap with this ND_BOX? Put the lowest cell addresses in ND_IBOX->min and the high...
int gbox_is_valid(const GBOX *gbox)
Return false if any of the dimensions is NaN or infinite.
Definition: g_box.c:174
#define ND_DIMS
The maximum number of dimensions our code can handle.
#define STATISTIC_SLOT_2D
static void nd_box_from_gbox(const GBOX *gbox, ND_BOX *nd_box)
Set the values of an ND_BOX from a GBOX.
#define LW_FAILURE
Definition: liblwgeom.h:64
static int nd_box_init(ND_BOX *a)
Zero out an ND_BOX.
double zmax
Definition: liblwgeom.h:281
static int nd_box_array_distribution(const ND_BOX **nd_boxes, int num_boxes, const ND_BOX *extent, int ndims, double *distribution)
Calculate how much a set of boxes is homogenously distributed or contentrated within one dimension...
static int nd_stats_value_index(const ND_STATS *stats, int *indexes)
Given a position in the n-d histogram (i,j,k) return the position in the 1-d values array...
float4 size[ND_DIMS]
static int nd_box_intersects(const ND_BOX *a, const ND_BOX *b, int ndims)
Return TRUE if ND_BOX a overlaps b, false otherwise.
static double total_double(const double *vals, int nvals)
Given double array, return sum of values.
int min[ND_DIMS]
static int nd_box_init_bounds(ND_BOX *a)
Prepare an ND_BOX for bounds calculation: set the maxes to the smallest thing possible and the mins t...
N-dimensional box index type.
static char * nd_box_to_json(const ND_BOX *nd_box, int ndims)
Convert an ND_BOX to a JSON string for printing.
float4 max[ND_DIMS]
#define STATISTIC_KIND_2D
int max[ND_DIMS]
struct ND_STATS_T ND_STATS
N-dimensional statistics structure.
float4 min[ND_DIMS]
static int nd_box_merge(const ND_BOX *source, ND_BOX *target)
Create a printable view of the ND_STATS histogram.
static int nd_box_expand(ND_BOX *nd_box, double expansion_factor)
Expand an ND_BOX ever so slightly.
double mmin
Definition: liblwgeom.h:282
#define SDFACTOR
double zmin
Definition: liblwgeom.h:280
static char * nd_stats_to_json(const ND_STATS *nd_stats)
Convert an ND_STATS to a JSON representation for external use.
double mmax
Definition: liblwgeom.h:283
#define STATISTIC_SLOT_ND
static double nd_box_ratio(const ND_BOX *b1, const ND_BOX *b2, int ndims)
Returns the proportion of b2 that is covered by b1.
N-dimensional statistics structure.
N-dimensional box type for calculations, to avoid doing explicit axis conversions from GBOX in all ca...
static int gbox_ndims(const GBOX *gbox)
Given that geodetic boxes are X/Y/Z regardless of the underlying geometry dimensionality and other bo...
#define STATISTIC_KIND_ND
Assign a number to the n-dimensional statistics kind.

Here is the call graph for this function:

Here is the caller graph for this function: