PostGIS  2.5.0beta2dev-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 j_oinsel) can then use the histogram

Definition at line 1346 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().

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