PostGIS  2.5.1dev-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 1359 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().

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