PostGIS  3.7.0dev-r@@SVN_REVISION@@

◆ compute_gserialized_stats_mode()

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 joinsel) can then use the histogram

Definition at line 1368 of file gserialized_estimate.c.

1370 {
1371  MemoryContext old_context;
1372  int d, i; /* Counters */
1373  int notnull_cnt = 0; /* # not null rows in the sample */
1374  int null_cnt = 0; /* # null rows in the sample */
1375  int histogram_features = 0; /* # rows that actually got counted in the histogram */
1376 
1377  ND_STATS *nd_stats; /* Our histogram */
1378  size_t nd_stats_size; /* Size to allocate */
1379 
1380  double total_width = 0; /* # of bytes used by sample */
1381  double total_cell_count = 0; /* # of cells in histogram affected by sample */
1382 
1383  ND_BOX sum; /* Sum of extents of sample boxes */
1384  ND_BOX avg; /* Avg of extents of sample boxes */
1385  ND_BOX stddev; /* StdDev of extents of sample boxes */
1386 
1387  const ND_BOX **sample_boxes; /* ND_BOXes for each of the sample features */
1388  ND_BOX sample_extent; /* Extent of the raw sample */
1389  int histo_size[ND_DIMS]; /* histogram nrows, ncols, etc */
1390  ND_BOX histo_extent; /* Spatial extent of the histogram */
1391  ND_BOX histo_extent_new; /* Temporary variable */
1392  int histo_cells_target; /* Number of cells we will shoot for, given the stats target */
1393  int histo_cells; /* Number of cells in the histogram */
1394  int histo_cells_new = 1; /* Temporary variable */
1395 
1396  int ndims = 2; /* Dimensionality of the sample */
1397  int histo_ndims = 0; /* Dimensionality of the histogram */
1398  double sample_distribution[ND_DIMS]; /* How homogeneous is distribution of sample in each axis? */
1399  double total_distribution; /* Total of sample_distribution */
1400 
1401  int stats_slot; /* What slot is this data going into? (2D vs ND) */
1402  int stats_kind; /* And this is what? (2D vs ND) */
1403 
1404  /* Initialize sum and stddev */
1405  nd_box_init(&sum);
1406  nd_box_init(&stddev);
1407  nd_box_init(&avg);
1408  nd_box_init(&histo_extent);
1409  nd_box_init(&histo_extent_new);
1410 
1411  /*
1412  * This is where gserialized_analyze_nd
1413  * should put its' custom parameters.
1414  */
1415  /* void *mystats = stats->extra_data; */
1416 
1417  POSTGIS_DEBUG(2, "compute_gserialized_stats called");
1418  POSTGIS_DEBUGF(3, " # sample_rows: %d", sample_rows);
1419  POSTGIS_DEBUGF(3, " estimate of total_rows: %.6g", total_rows);
1420 
1421  /*
1422  * We might need less space, but don't think
1423  * its worth saving...
1424  */
1425  sample_boxes = palloc(sizeof(ND_BOX*) * sample_rows);
1426 
1427  /*
1428  * First scan:
1429  * o read boxes
1430  * o find dimensionality of the sample
1431  * o find extent of the sample
1432  * o count null-infinite/not-null values
1433  * o compute total_width
1434  * o compute total features's box area (for avgFeatureArea)
1435  * o sum features box coordinates (for standard deviation)
1436  */
1437  for ( i = 0; i < sample_rows; i++ )
1438  {
1439  Datum datum;
1440  GBOX gbox = {0};
1441  ND_BOX *nd_box;
1442  bool is_null;
1443 
1444  datum = fetchfunc(stats, i, &is_null);
1445 
1446  /* Skip all NULLs. */
1447  if ( is_null )
1448  {
1449  POSTGIS_DEBUGF(4, " skipped null geometry %d", i);
1450  null_cnt++;
1451  continue;
1452  }
1453 
1454  /* Read the bounds from the gserialized. */
1455  if (LW_FAILURE == gserialized_datum_get_gbox_p(datum, &gbox))
1456  {
1457  /* Skip empties too. */
1458  POSTGIS_DEBUGF(3, " skipped empty geometry %d", i);
1459  continue;
1460  }
1461 
1462  /* If we're in 2D mode, zero out the higher dimensions for "safety" */
1463  if ( mode == 2 )
1464  gbox.zmin = gbox.zmax = gbox.mmin = gbox.mmax = 0.0;
1465 
1466  /* Check bounds for validity (finite and not NaN) */
1467  if ( ! gbox_is_valid(&gbox) )
1468  {
1469  POSTGIS_DEBUGF(3, " skipped infinite/nan geometry %d", i);
1470  continue;
1471  }
1472 
1473  /*
1474  * In N-D mode, set the ndims to the maximum dimensionality found
1475  * in the sample. Otherwise, leave at ndims == 2.
1476  */
1477  if ( mode != 2 )
1478  ndims = Max(gbox_ndims(&gbox), ndims);
1479 
1480  /* Convert gbox to n-d box */
1481  nd_box = palloc(sizeof(ND_BOX));
1482  nd_box_from_gbox(&gbox, nd_box);
1483 
1484  /* Cache n-d bounding box */
1485  sample_boxes[notnull_cnt] = nd_box;
1486 
1487  /* Initialize sample extent before merging first entry */
1488  if ( ! notnull_cnt )
1489  nd_box_init_bounds(&sample_extent);
1490 
1491  /* Add current sample to overall sample extent */
1492  nd_box_merge(nd_box, &sample_extent);
1493 
1494  /* How many bytes does this sample use? */
1495  total_width += toast_raw_datum_size(datum);
1496 
1497  /* Add bounds coordinates to sums for stddev calculation */
1498  for ( d = 0; d < ndims; d++ )
1499  {
1500  sum.min[d] += nd_box->min[d];
1501  sum.max[d] += nd_box->max[d];
1502  }
1503 
1504  /* Increment our "good feature" count */
1505  notnull_cnt++;
1506 
1507  /* Give backend a chance of interrupting us */
1508 #if POSTGIS_PGSQL_VERSION >= 180
1509  vacuum_delay_point(true);
1510 #else
1511  vacuum_delay_point();
1512 #endif
1513  }
1514 
1515  /*
1516  * We'll build a histogram having stats->attr->attstattarget
1517  * (default 100) cells on each side, within reason...
1518  * we'll use ndims*100000 as the maximum number of cells.
1519  * Also, if we're sampling a relatively small table, we'll try to ensure that
1520  * we have a smaller grid.
1521  */
1522 #if POSTGIS_PGSQL_VERSION >= 170
1523  histo_cells_target = (int)pow((double)(stats->attstattarget), (double)ndims);
1524  POSTGIS_DEBUGF(3, " stats->attstattarget: %d", stats->attstattarget);
1525 #else
1526  histo_cells_target = (int)pow((double)(stats->attr->attstattarget), (double)ndims);
1527  POSTGIS_DEBUGF(3, " stats->attr->attstattarget: %d", stats->attr->attstattarget);
1528 #endif
1529  histo_cells_target = Min(histo_cells_target, ndims * 100000);
1530  histo_cells_target = Min(histo_cells_target, (int)(10 * ndims * total_rows));
1531  POSTGIS_DEBUGF(3, " target # of histogram cells: %d", histo_cells_target);
1532 
1533  /* If there's no useful features, we can't work out stats */
1534  if ( ! notnull_cnt )
1535  {
1536  stats->stats_valid = false;
1537  return;
1538  }
1539 
1540  POSTGIS_DEBUGF(3, " sample_extent: %s", nd_box_to_json(&sample_extent, ndims));
1541 
1542  /*
1543  * Second scan:
1544  * o compute standard deviation
1545  */
1546  for ( d = 0; d < ndims; d++ )
1547  {
1548  /* Calculate average bounds values */
1549  avg.min[d] = sum.min[d] / notnull_cnt;
1550  avg.max[d] = sum.max[d] / notnull_cnt;
1551 
1552  /* Calculate standard deviation for this dimension bounds */
1553  for ( i = 0; i < notnull_cnt; i++ )
1554  {
1555  const ND_BOX *ndb = sample_boxes[i];
1556  stddev.min[d] += (ndb->min[d] - avg.min[d]) * (ndb->min[d] - avg.min[d]);
1557  stddev.max[d] += (ndb->max[d] - avg.max[d]) * (ndb->max[d] - avg.max[d]);
1558  }
1559  stddev.min[d] = sqrt(stddev.min[d] / notnull_cnt);
1560  stddev.max[d] = sqrt(stddev.max[d] / notnull_cnt);
1561 
1562  /* Histogram bounds for this dimension bounds is avg +/- SDFACTOR * stdev */
1563  histo_extent.min[d] = Max(avg.min[d] - SDFACTOR * stddev.min[d], sample_extent.min[d]);
1564  histo_extent.max[d] = Min(avg.max[d] + SDFACTOR * stddev.max[d], sample_extent.max[d]);
1565  }
1566 
1567  /*
1568  * Third scan:
1569  * o skip hard deviants
1570  * o compute new histogram box
1571  */
1572  nd_box_init_bounds(&histo_extent_new);
1573  for ( i = 0; i < notnull_cnt; i++ )
1574  {
1575  const ND_BOX *ndb = sample_boxes[i];
1576  /* Skip any hard deviants (boxes entirely outside our histo_extent */
1577  if ( ! nd_box_intersects(&histo_extent, ndb, ndims) )
1578  {
1579  POSTGIS_DEBUGF(4, " feature %d is a hard deviant, skipped", i);
1580  sample_boxes[i] = NULL;
1581  continue;
1582  }
1583  /* Expand our new box to fit all the other features. */
1584  nd_box_merge(ndb, &histo_extent_new);
1585  }
1586  /*
1587  * Expand the box slightly (1%) to avoid edge effects
1588  * with objects that are on the boundary
1589  */
1590  nd_box_expand(&histo_extent_new, 0.01);
1591  histo_extent = histo_extent_new;
1592 
1593  /*
1594  * How should we allocate our histogram cells to the
1595  * different dimensions? We can't do it by raw dimensional width,
1596  * because in x/y/z space, the z can have different units
1597  * from the x/y. Similarly for x/y/t space.
1598  * So, we instead calculate how much features overlap
1599  * each other in their dimension to figure out which
1600  * dimensions have useful selectivity characteristics (more
1601  * variability in density) and therefore would find
1602  * more cells useful (to distinguish between dense places and
1603  * homogeneous places).
1604  */
1605  nd_box_array_distribution(sample_boxes, notnull_cnt, &histo_extent, ndims,
1606  sample_distribution);
1607 
1608  /*
1609  * The sample_distribution array now tells us how spread out the
1610  * data is in each dimension, so we use that data to allocate
1611  * the histogram cells we have available.
1612  * At this point, histo_cells_target is the approximate target number
1613  * of cells.
1614  */
1615 
1616  /*
1617  * Some dimensions have basically a uniform distribution, we want
1618  * to allocate no cells to those dimensions, only to dimensions
1619  * that have some interesting differences in data distribution.
1620  * Here we count up the number of interesting dimensions
1621  */
1622  for ( d = 0; d < ndims; d++ )
1623  {
1624  if ( sample_distribution[d] > 0 )
1625  histo_ndims++;
1626  }
1627 
1628  if ( histo_ndims == 0 )
1629  {
1630  /* Special case: all our dimensions had low variability! */
1631  /* We just divide the cells up evenly */
1632  POSTGIS_DEBUG(3, " special case: no axes have variability");
1633  histo_cells_new = 1;
1634  for ( d = 0; d < ndims; d++ )
1635  {
1636  histo_size[d] = (int)pow((double)histo_cells_target, 1/(double)ndims);
1637  if ( ! histo_size[d] )
1638  histo_size[d] = 1;
1639  POSTGIS_DEBUGF(3, " histo_size[d]: %d", histo_size[d]);
1640  histo_cells_new *= histo_size[d];
1641  }
1642  POSTGIS_DEBUGF(3, " histo_cells_new: %d", histo_cells_new);
1643  }
1644  else
1645  {
1646  /*
1647  * We're going to express the amount of variability in each dimension
1648  * as a proportion of the total variability and allocate cells in that
1649  * dimension relative to that proportion.
1650  */
1651  POSTGIS_DEBUG(3, " allocating histogram axes based on axis variability");
1652  total_distribution = total_double(sample_distribution, ndims); /* First get the total */
1653  POSTGIS_DEBUGF(3, " total_distribution: %.8g", total_distribution);
1654  histo_cells_new = 1; /* For the number of cells in the final histogram */
1655  for ( d = 0; d < ndims; d++ )
1656  {
1657  if ( sample_distribution[d] == 0 ) /* Uninteresting dimensions don't get any room */
1658  {
1659  histo_size[d] = 1;
1660  }
1661  else /* Interesting dimension */
1662  {
1663  /* How does this dims variability compare to the total? */
1664  float edge_ratio = (float)sample_distribution[d] / (float)total_distribution;
1665  /*
1666  * Scale the target cells number by the # of dims and ratio,
1667  * then take the appropriate root to get the estimated number of cells
1668  * on this axis (eg, pow(0.5) for 2d, pow(0.333) for 3d, pow(0.25) for 4d)
1669  */
1670  histo_size[d] = (int)pow((double)histo_cells_target * histo_ndims * edge_ratio, 1/(double)histo_ndims);
1671  /* If something goes awry, just give this dim one slot */
1672  if ( ! histo_size[d] )
1673  histo_size[d] = 1;
1674  }
1675  histo_cells_new *= histo_size[d];
1676  }
1677  POSTGIS_DEBUGF(3, " histo_cells_new: %d", histo_cells_new);
1678  }
1679 
1680  /* Update histo_cells to the actual number of cells we need to allocate */
1681  histo_cells = histo_cells_new;
1682  POSTGIS_DEBUGF(3, " histo_cells: %d", histo_cells);
1683 
1684  /*
1685  * Create the histogram (ND_STATS) in the stats memory context
1686  */
1687  old_context = MemoryContextSwitchTo(stats->anl_context);
1688  nd_stats_size = sizeof(ND_STATS) + ((histo_cells - 1) * sizeof(float4));
1689  nd_stats = palloc(nd_stats_size);
1690  memset(nd_stats, 0, nd_stats_size); /* Initialize all values to 0 */
1691  MemoryContextSwitchTo(old_context);
1692 
1693  /* Initialize the #ND_STATS objects */
1694  nd_stats->ndims = ndims;
1695  nd_stats->extent = histo_extent;
1696  nd_stats->sample_features = sample_rows;
1697  nd_stats->table_features = total_rows;
1698  nd_stats->not_null_features = notnull_cnt;
1699  /* Copy in the histogram dimensions */
1700  for ( d = 0; d < ndims; d++ )
1701  nd_stats->size[d] = histo_size[d];
1702 
1703  /*
1704  * Fourth scan:
1705  * o fill histogram values with the proportion of
1706  * features' bbox overlaps: a feature's bvol
1707  * can fully overlap (1) or partially overlap
1708  * (fraction of 1) an histogram cell.
1709  *
1710  * Note that we are filling each cell with the "portion of
1711  * the feature's box that overlaps the cell". So, if we sum
1712  * up the values in the histogram, we could get the
1713  * histogram feature count.
1714  *
1715  */
1716  for ( i = 0; i < notnull_cnt; i++ )
1717  {
1718  const ND_BOX *nd_box;
1719  ND_IBOX nd_ibox;
1720  int at[ND_DIMS];
1721  double num_cells = 0;
1722  double min[ND_DIMS] = {0.0, 0.0, 0.0, 0.0};
1723  double max[ND_DIMS] = {0.0, 0.0, 0.0, 0.0};
1724  double cellsize[ND_DIMS] = {0.0, 0.0, 0.0, 0.0};
1725 
1726  nd_box = sample_boxes[i];
1727  if ( ! nd_box ) continue; /* Skip Null'ed out hard deviants */
1728 
1729  /* Give backend a chance of interrupting us */
1730 #if POSTGIS_PGSQL_VERSION >= 180
1731  vacuum_delay_point(true);
1732 #else
1733  vacuum_delay_point();
1734 #endif
1735 
1736  /* Find the cells that overlap with this box and put them into the ND_IBOX */
1737  nd_box_overlap(nd_stats, nd_box, &nd_ibox);
1738  memset(at, 0, sizeof(int)*ND_DIMS);
1739 
1740  POSTGIS_DEBUGF(3, " feature %d: ibox (%d, %d, %d, %d) (%d, %d, %d, %d)", i,
1741  nd_ibox.min[0], nd_ibox.min[1], nd_ibox.min[2], nd_ibox.min[3],
1742  nd_ibox.max[0], nd_ibox.max[1], nd_ibox.max[2], nd_ibox.max[3]);
1743 
1744  for ( d = 0; d < nd_stats->ndims; d++ )
1745  {
1746  /* Initialize the starting values */
1747  at[d] = nd_ibox.min[d];
1748  min[d] = nd_stats->extent.min[d];
1749  max[d] = nd_stats->extent.max[d];
1750  cellsize[d] = (max[d] - min[d])/(nd_stats->size[d]);
1751  }
1752 
1753  /*
1754  * Move through all the overlapped histogram cells values and
1755  * add the box overlap proportion to them.
1756  */
1757  do
1758  {
1759  ND_BOX nd_cell = { {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0} };
1760  double ratio;
1761  /* Create a box for this histogram cell */
1762  for ( d = 0; d < nd_stats->ndims; d++ )
1763  {
1764  nd_cell.min[d] = min[d] + (at[d]+0) * cellsize[d];
1765  nd_cell.max[d] = min[d] + (at[d]+1) * cellsize[d];
1766  }
1767 
1768  /*
1769  * If a feature box is completely inside one cell the ratio will be
1770  * 1.0. If a feature box is 50% in two cells, each cell will get
1771  * 0.5 added on.
1772  */
1773  ratio = nd_box_ratio(&nd_cell, nd_box, nd_stats->ndims);
1774  nd_stats->value[nd_stats_value_index(nd_stats, at)] += ratio;
1775  num_cells += ratio;
1776  POSTGIS_DEBUGF(3, " ratio (%.8g) num_cells (%.8g)", ratio, num_cells);
1777  POSTGIS_DEBUGF(3, " at (%d, %d, %d, %d)", at[0], at[1], at[2], at[3]);
1778  }
1779  while ( nd_increment(&nd_ibox, nd_stats->ndims, at) );
1780 
1781  /* Keep track of overall number of overlaps counted */
1782  total_cell_count += num_cells;
1783  /* How many features have we added to this histogram? */
1784  histogram_features++;
1785  }
1786 
1787  POSTGIS_DEBUGF(3, " histogram_features: %d", histogram_features);
1788  POSTGIS_DEBUGF(3, " sample_rows: %d", sample_rows);
1789  POSTGIS_DEBUGF(3, " table_rows: %.6g", total_rows);
1790 
1791  /* Error out if we got no sample information */
1792  if ( ! histogram_features )
1793  {
1794  POSTGIS_DEBUG(3, " no stats have been gathered");
1795  elog(NOTICE, " no features lie in the stats histogram, invalid stats");
1796  stats->stats_valid = false;
1797  return;
1798  }
1799 
1800  nd_stats->histogram_features = histogram_features;
1801  nd_stats->histogram_cells = histo_cells;
1802  nd_stats->cells_covered = total_cell_count;
1803 
1804  /* Put this histogram data into the right slot/kind */
1805  if ( mode == 2 )
1806  {
1807  stats_slot = STATISTIC_SLOT_2D;
1808  stats_kind = STATISTIC_KIND_2D;
1809  }
1810  else
1811  {
1812  stats_slot = STATISTIC_SLOT_ND;
1813  stats_kind = STATISTIC_KIND_ND;
1814  }
1815 
1816  /* Write the statistics data */
1817  stats->stakind[stats_slot] = stats_kind;
1818  stats->staop[stats_slot] = InvalidOid;
1819  stats->stanumbers[stats_slot] = (float4*)nd_stats;
1820  stats->numnumbers[stats_slot] = nd_stats_size/sizeof(float4);
1821  stats->stanullfrac = (float4)null_cnt/sample_rows;
1822  stats->stawidth = total_width/notnull_cnt;
1823  stats->stadistinct = -1.0;
1824  stats->stats_valid = true;
1825 
1826  POSTGIS_DEBUGF(3, " out: slot 0: kind %d (STATISTIC_KIND_ND)", stats->stakind[0]);
1827  POSTGIS_DEBUGF(3, " out: slot 0: op %d (InvalidOid)", stats->staop[0]);
1828  POSTGIS_DEBUGF(3, " out: slot 0: numnumbers %d", stats->numnumbers[0]);
1829  POSTGIS_DEBUGF(3, " out: null fraction: %f=%d/%d", stats->stanullfrac, null_cnt, sample_rows);
1830  POSTGIS_DEBUGF(3, " out: average width: %d bytes", stats->stawidth);
1831  POSTGIS_DEBUG (3, " out: distinct values: all (no check done)");
1832  POSTGIS_DEBUGF(3, " out: %s", nd_stats_to_json(nd_stats));
1833  /*
1834  POSTGIS_DEBUGF(3, " out histogram:\n%s", nd_stats_to_grid(nd_stats));
1835  */
1836 
1837  return;
1838 }
int gbox_is_valid(const GBOX *gbox)
Return false if any of the dimensions is NaN or infinite.
Definition: gbox.c:197
struct ND_STATS_T ND_STATS
N-dimensional statistics structure.
static char * nd_stats_to_json(const ND_STATS *nd_stats)
Convert an ND_STATS to a JSON representation for external use.
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 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...
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,...
#define STATISTIC_SLOT_ND
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 ND_DIMS
The maximum number of dimensions our code can handle.
#define STATISTIC_KIND_2D
static int nd_box_merge(const ND_BOX *source, ND_BOX *target)
Expand the bounds of target to include source.
#define STATISTIC_KIND_ND
static double total_double(const double *vals, int nvals)
Given double array, return sum of values.
#define SDFACTOR
static void nd_box_from_gbox(const GBOX *gbox, ND_BOX *nd_box)
Set the values of an ND_BOX from a GBOX.
static int nd_box_init(ND_BOX *a)
Zero out an ND_BOX.
static int nd_box_expand(ND_BOX *nd_box, double expansion_factor)
Expand an ND_BOX ever so slightly.
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...
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.
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.
static char * nd_box_to_json(const ND_BOX *nd_box, int ndims)
Convert an ND_BOX to a JSON string for printing.
#define STATISTIC_SLOT_2D
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 homogeneously distributed or contentrated within one dimension,...
int gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox)
Given a GSERIALIZED datum, as quickly as possible (peaking into the top of the memory) return the gbo...
#define LW_FAILURE
Definition: liblwgeom.h:96
double zmax
Definition: liblwgeom.h:359
double zmin
Definition: liblwgeom.h:358
double mmax
Definition: liblwgeom.h:361
double mmin
Definition: liblwgeom.h:360
float4 max[ND_DIMS]
float4 min[ND_DIMS]
N-dimensional box type for calculations, to avoid doing explicit axis conversions from GBOX in all ca...
int max[ND_DIMS]
int min[ND_DIMS]
N-dimensional box index type.
float4 size[ND_DIMS]
N-dimensional statistics structure.

References ND_STATS_T::cells_covered, ND_STATS_T::extent, gbox_is_valid(), gbox_ndims(), gserialized_datum_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().

Here is the call graph for this function:
Here is the caller graph for this function: