PostGIS  3.3.9dev-r@@SVN_REVISION@@

◆ lwpoly_to_points()

LWMPOINT* lwpoly_to_points ( const LWPOLY lwpoly,
uint32_t  npoints,
int32_t  seed 
)

Definition at line 1670 of file liblwgeom/lwgeom_geos.c.

1671 {
1672  double area, bbox_area, bbox_width, bbox_height;
1673  GBOX bbox;
1674  const LWGEOM* lwgeom = (LWGEOM*)lwpoly;
1675  uint32_t sample_npoints, sample_sqrt, sample_width, sample_height;
1676  double sample_cell_size;
1677  uint32_t i, j, n;
1678  uint32_t iterations = 0;
1679  uint32_t npoints_generated = 0;
1680  uint32_t npoints_tested = 0;
1681  GEOSGeometry* g;
1682  const GEOSPreparedGeometry* gprep;
1683  GEOSGeometry* gpt;
1684  GEOSCoordSequence* gseq;
1685  LWMPOINT* mpt;
1686  int32_t srid = lwgeom_get_srid(lwgeom);
1687  int done = 0;
1688  int* cells;
1689  const size_t size = 2 * sizeof(int);
1690  char tmp[2 * sizeof(int)];
1691  const size_t stride = 2 * sizeof(int);
1692 
1693  if (lwgeom_get_type(lwgeom) != POLYGONTYPE)
1694  {
1695  lwerror("%s: only polygons supported", __func__);
1696  return NULL;
1697  }
1698 
1699  if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL;
1700 
1701  if (!lwpoly->bbox)
1702  lwgeom_calculate_gbox(lwgeom, &bbox);
1703  else
1704  bbox = *(lwpoly->bbox);
1705 
1706  area = lwpoly_area(lwpoly);
1707  bbox_width = bbox.xmax - bbox.xmin;
1708  bbox_height = bbox.ymax - bbox.ymin;
1709  bbox_area = bbox_width * bbox_height;
1710 
1711  if (area == 0.0 || bbox_area == 0.0)
1712  {
1713  lwerror("%s: zero area input polygon, TBD", __func__);
1714  return NULL;
1715  }
1716 
1717  /* Gross up our test set a bit (but not too much) to increase
1718  * odds of getting coverage in one pass */
1719  sample_npoints = npoints * FP_MIN(bbox_area / area, 10000.0);
1720 
1721  /* We're going to generate points using a sample grid as described
1722  * http://lin-ear-th-inking.blogspot.ca/2010/05/more-random-points-in-jts.html to try and get a more uniform
1723  * "random" set of points. So we have to figure out how to stick a grid into our box */
1724  sample_sqrt = lround(sqrt(sample_npoints));
1725  if (sample_sqrt == 0)
1726  sample_sqrt = 1;
1727 
1728  /* Calculate the grids we're going to randomize within */
1729  if (bbox_width > bbox_height)
1730  {
1731  sample_width = sample_sqrt;
1732  sample_height = ceil((double)sample_npoints / (double)sample_width);
1733  sample_cell_size = bbox_width / sample_width;
1734  }
1735  else
1736  {
1737  sample_height = sample_sqrt;
1738  sample_width = ceil((double)sample_npoints / (double)sample_height);
1739  sample_cell_size = bbox_height / sample_height;
1740  }
1741 
1742  /* Prepare the polygon for fast true/false testing */
1743  initGEOS(lwnotice, lwgeom_geos_error);
1744  g = (GEOSGeometry*)LWGEOM2GEOS(lwgeom, 0);
1745  if (!g)
1746  {
1747  lwerror("%s: Geometry could not be converted to GEOS: %s", __func__, lwgeom_geos_errmsg);
1748  return NULL;
1749  }
1750  gprep = GEOSPrepare(g);
1751 
1752  /* Get an empty multi-point ready to return */
1753  mpt = lwmpoint_construct_empty(srid, 0, 0);
1754 
1755  /* Initiate random number generator.
1756  * Repeatable numbers are generated with seed values >= 1.
1757  * When seed is zero and has not previously been set, it is based on
1758  * Unix time (seconds) and process ID. */
1759  lwrandom_set_seed(seed);
1760 
1761  /* Now we fill in an array of cells, and then shuffle that array, */
1762  /* so we can visit the cells in random order to avoid visual ugliness */
1763  /* caused by visiting them sequentially */
1764  cells = lwalloc(2 * sizeof(int) * sample_height * sample_width);
1765  for (i = 0; i < sample_width; i++)
1766  {
1767  for (j = 0; j < sample_height; j++)
1768  {
1769  cells[2 * (i * sample_height + j)] = i;
1770  cells[2 * (i * sample_height + j) + 1] = j;
1771  }
1772  }
1773 
1774  /* Fisher-Yates shuffle */
1775  n = sample_height * sample_width;
1776  if (n > 1)
1777  {
1778  for (i = n - 1; i > 0; i--)
1779  {
1780  size_t j = (size_t)(lwrandom_uniform() * (i + 1));
1781 
1782  memcpy(tmp, (char *)cells + j * stride, size);
1783  memcpy((char *)cells + j * stride, (char *)cells + i * stride, size);
1784  memcpy((char *)cells + i * stride, tmp, size);
1785  }
1786  }
1787 
1788  /* Start testing points */
1789  while (npoints_generated < npoints)
1790  {
1791  iterations++;
1792  for (i = 0; i < sample_width * sample_height; i++)
1793  {
1794  int contains = 0;
1795  double y = bbox.ymin + cells[2 * i] * sample_cell_size;
1796  double x = bbox.xmin + cells[2 * i + 1] * sample_cell_size;
1797  x += lwrandom_uniform() * sample_cell_size;
1798  y += lwrandom_uniform() * sample_cell_size;
1799  if (x >= bbox.xmax || y >= bbox.ymax) continue;
1800 
1801  gseq = GEOSCoordSeq_create(1, 2);
1802 #if POSTGIS_GEOS_VERSION < 30800
1803  GEOSCoordSeq_setX(gseq, 0, x);
1804  GEOSCoordSeq_setY(gseq, 0, y);
1805 #else
1806  GEOSCoordSeq_setXY(gseq, 0, x, y);
1807 #endif
1808  gpt = GEOSGeom_createPoint(gseq);
1809 
1810  contains = GEOSPreparedIntersects(gprep, gpt);
1811 
1812  GEOSGeom_destroy(gpt);
1813 
1814  if (contains == 2)
1815  {
1816  GEOSPreparedGeom_destroy(gprep);
1817  GEOSGeom_destroy(g);
1818  lwerror("%s: GEOS exception on PreparedContains: %s", __func__, lwgeom_geos_errmsg);
1819  return NULL;
1820  }
1821  if (contains)
1822  {
1823  npoints_generated++;
1824  mpt = lwmpoint_add_lwpoint(mpt, lwpoint_make2d(srid, x, y));
1825  if (npoints_generated == npoints)
1826  {
1827  done = 1;
1828  break;
1829  }
1830  }
1831 
1832  /* Short-circuit check for ctrl-c occasionally */
1833  npoints_tested++;
1834  if (npoints_tested % 10000 == 0)
1835  LW_ON_INTERRUPT(GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); return NULL);
1836 
1837  if (done) break;
1838  }
1839  if (done || iterations > 100) break;
1840  }
1841 
1842  GEOSPreparedGeom_destroy(gprep);
1843  GEOSGeom_destroy(g);
1844  lwfree(cells);
1845 
1846  return mpt;
1847 }
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
void lwgeom_geos_error(const char *fmt,...)
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:927
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition: lwpoint.c:163
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition: lwmpoint.c:45
void lwfree(void *mem)
Definition: lwutil.c:242
#define POLYGONTYPE
Definition: liblwgeom.h:119
LWMPOINT * lwmpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwmpoint.c:39
int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
Calculate bounding box of a geometry, automatically taking into account whether it is cartesian or ge...
Definition: lwgeom.c:755
void * lwalloc(size_t size)
Definition: lwutil.c:227
#define LW_ON_INTERRUPT(x)
#define FP_MIN(A, B)
double lwpoly_area(const LWPOLY *poly)
Find the area of the outer ring - sum (area of inner rings).
Definition: lwpoly.c:434
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition: lwutil.c:177
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:145
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwinline.h:203
void lwrandom_set_seed(int32_t seed)
Definition: lwrandom.c:48
double lwrandom_uniform(void)
Definition: lwrandom.c:94
Datum contains(PG_FUNCTION_ARGS)
double ymax
Definition: liblwgeom.h:372
double xmax
Definition: liblwgeom.h:370
double ymin
Definition: liblwgeom.h:371
double xmin
Definition: liblwgeom.h:369
GBOX * bbox
Definition: liblwgeom.h:533

References LWPOLY::bbox, contains(), FP_MIN, LW_ON_INTERRUPT, lwalloc(), lwerror(), lwfree(), LWGEOM2GEOS(), lwgeom_calculate_gbox(), lwgeom_geos_errmsg, lwgeom_geos_error(), lwgeom_get_srid(), lwgeom_get_type(), lwgeom_is_empty(), lwmpoint_add_lwpoint(), lwmpoint_construct_empty(), lwnotice(), lwpoint_make2d(), lwpoly_area(), lwrandom_set_seed(), lwrandom_uniform(), POLYGONTYPE, pixval::x, GBOX::xmax, GBOX::xmin, pixval::y, GBOX::ymax, and GBOX::ymin.

Referenced by lwgeom_to_points(), and lwmpoly_to_points().

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