PostGIS  2.4.9dev-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 1325 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().

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