PostGIS  2.5.0dev-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 1343 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().

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

Here is the call graph for this function:

Here is the caller graph for this function: