PostGIS  2.5.0dev-r@@SVN_REVISION@@

◆ lwpoly_to_points()

LWMPOINT* lwpoly_to_points ( const LWPOLY lwpoly,
uint32_t  npoints 
)

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

References area(), LWPOLY::bbox, contains(), 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(), POLYGONTYPE, pixval::x, GBOX::xmax, GBOX::xmin, pixval::y, GBOX::ymax, and GBOX::ymin.

Referenced by lwgeom_to_points(), and lwmpoly_to_points().

1573 {
1574  double area, bbox_area, bbox_width, bbox_height;
1575  GBOX bbox;
1576  const LWGEOM* lwgeom = (LWGEOM*)lwpoly;
1577  uint32_t sample_npoints, sample_sqrt, sample_width, sample_height;
1578  double sample_cell_size;
1579  uint32_t i, j, n;
1580  uint32_t iterations = 0;
1581  uint32_t npoints_generated = 0;
1582  uint32_t npoints_tested = 0;
1583  GEOSGeometry* g;
1584  const GEOSPreparedGeometry* gprep;
1585  GEOSGeometry* gpt;
1586  GEOSCoordSequence* gseq;
1587  LWMPOINT* mpt;
1588  int srid = lwgeom_get_srid(lwgeom);
1589  int done = 0;
1590  int* cells;
1591  const size_t size = 2 * sizeof(int);
1592  char tmp[2 * sizeof(int)];
1593  const size_t stride = 2 * sizeof(int);
1594 
1595  if (lwgeom_get_type(lwgeom) != POLYGONTYPE)
1596  {
1597  lwerror("%s: only polygons supported", __func__);
1598  return NULL;
1599  }
1600 
1601  if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL;
1602 
1603  if (!lwpoly->bbox)
1604  lwgeom_calculate_gbox(lwgeom, &bbox);
1605 
1606  else
1607  bbox = *(lwpoly->bbox);
1608  area = lwpoly_area(lwpoly);
1609  bbox_width = bbox.xmax - bbox.xmin;
1610  bbox_height = bbox.ymax - bbox.ymin;
1611  bbox_area = bbox_width * bbox_height;
1612 
1613  if (area == 0.0 || bbox_area == 0.0)
1614  {
1615  lwerror("%s: zero area input polygon, TBD", __func__);
1616  return NULL;
1617  }
1618 
1619  /* Gross up our test set a bit to increase odds of getting coverage in one pass */
1620  sample_npoints = npoints * bbox_area / area;
1621 
1622  /* We're going to generate points using a sample grid as described
1623  * http://lin-ear-th-inking.blogspot.ca/2010/05/more-random-points-in-jts.html to try and get a more uniform
1624  * "random" set of points. So we have to figure out how to stick a grid into our box */
1625  sample_sqrt = lround(sqrt(sample_npoints));
1626  if (sample_sqrt == 0) sample_sqrt = 1;
1627 
1628  /* Calculate the grids we're going to randomize within */
1629  if (bbox_width > bbox_height)
1630  {
1631  sample_width = sample_sqrt;
1632  sample_height = ceil((double)sample_npoints / (double)sample_width);
1633  sample_cell_size = bbox_width / sample_width;
1634  }
1635  else
1636  {
1637  sample_height = sample_sqrt;
1638  sample_width = ceil((double)sample_npoints / (double)sample_height);
1639  sample_cell_size = bbox_height / sample_height;
1640  }
1641 
1642  /* Prepare the polygon for fast true/false testing */
1643  initGEOS(lwnotice, lwgeom_geos_error);
1644  g = (GEOSGeometry*)LWGEOM2GEOS(lwgeom, 0);
1645  if (!g)
1646  {
1647  lwerror("%s: Geometry could not be converted to GEOS: %s", __func__, lwgeom_geos_errmsg);
1648  return NULL;
1649  }
1650  gprep = GEOSPrepare(g);
1651 
1652  /* Get an empty multi-point ready to return */
1653  mpt = lwmpoint_construct_empty(srid, 0, 0);
1654 
1655  /* Init random number generator */
1656  srand(time(NULL));
1657 
1658  /* Now we fill in an array of cells, and then shuffle that array, */
1659  /* so we can visit the cells in random order to avoid visual ugliness */
1660  /* caused by visiting them sequentially */
1661  cells = lwalloc(2 * sizeof(int) * sample_height * sample_width);
1662  for (i = 0; i < sample_width; i++)
1663  {
1664  for (j = 0; j < sample_height; j++)
1665  {
1666  cells[2 * (i * sample_height + j)] = i;
1667  cells[2 * (i * sample_height + j) + 1] = j;
1668  }
1669  }
1670 
1671  /* shuffle */
1672  {
1673  n = sample_height * sample_width;
1674  if (n > 1)
1675  {
1676  for (i = 0; i < n - 1; ++i)
1677  {
1678  size_t rnd = (size_t)rand();
1679  size_t j = i + rnd / (RAND_MAX / (n - i) + 1);
1680 
1681  memcpy(tmp, (char*)cells + j * stride, size);
1682  memcpy((char*)cells + j * stride, (char*)cells + i * stride, size);
1683  memcpy((char*)cells + i * stride, tmp, size);
1684  }
1685  }
1686  }
1687 
1688  /* Start testing points */
1689  while (npoints_generated < npoints)
1690  {
1691  iterations++;
1692  for (i = 0; i < sample_width * sample_height; i++)
1693  {
1694  int contains = 0;
1695  double y = bbox.ymin + cells[2 * i] * sample_cell_size;
1696  double x = bbox.xmin + cells[2 * i + 1] * sample_cell_size;
1697  x += rand() * sample_cell_size / RAND_MAX;
1698  y += rand() * sample_cell_size / RAND_MAX;
1699  if (x >= bbox.xmax || y >= bbox.ymax) continue;
1700 
1701  gseq = GEOSCoordSeq_create(1, 2);
1702  GEOSCoordSeq_setX(gseq, 0, x);
1703  GEOSCoordSeq_setY(gseq, 0, y);
1704  gpt = GEOSGeom_createPoint(gseq);
1705 
1706  contains = GEOSPreparedIntersects(gprep, gpt);
1707 
1708  GEOSGeom_destroy(gpt);
1709 
1710  if (contains == 2)
1711  {
1712  GEOSPreparedGeom_destroy(gprep);
1713  GEOSGeom_destroy(g);
1714  lwerror("%s: GEOS exception on PreparedContains: %s", __func__, lwgeom_geos_errmsg);
1715  return NULL;
1716  }
1717  if (contains)
1718  {
1719  npoints_generated++;
1720  mpt = lwmpoint_add_lwpoint(mpt, lwpoint_make2d(srid, x, y));
1721  if (npoints_generated == npoints)
1722  {
1723  done = 1;
1724  break;
1725  }
1726  }
1727 
1728  /* Short-circuit check for ctrl-c occasionally */
1729  npoints_tested++;
1730  if (npoints_tested % 10000 == 0)
1731  LW_ON_INTERRUPT(GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); return NULL);
1732 
1733  if (done) break;
1734  }
1735  if (done || iterations > 100) break;
1736  }
1737 
1738  GEOSPreparedGeom_destroy(gprep);
1739  GEOSGeom_destroy(g);
1740  lwfree(cells);
1741 
1742  return mpt;
1743 }
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition: lwutil.c:177
void lwfree(void *mem)
Definition: lwutil.c:244
uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwgeom.c:922
#define POLYGONTYPE
Definition: liblwgeom.h:86
LWPOINT * lwpoint_make2d(int srid, double x, double y)
Definition: lwpoint.c:163
Datum area(PG_FUNCTION_ARGS)
double xmax
Definition: liblwgeom.h:292
Datum contains(PG_FUNCTION_ARGS)
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:915
#define LW_ON_INTERRUPT(x)
GBOX * bbox
Definition: liblwgeom.h:452
unsigned int uint32_t
Definition: uthash.h:78
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:745
double ymin
Definition: liblwgeom.h:293
double xmin
Definition: liblwgeom.h:291
void lwgeom_geos_error(const char *fmt,...)
double lwpoly_area(const LWPOLY *poly)
Find the area of the outer ring - sum (area of inner rings).
Definition: lwpoly.c:441
LWMPOINT * lwmpoint_construct_empty(int srid, char hasz, char hasm)
Definition: lwmpoint.c:39
double ymax
Definition: liblwgeom.h:294
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, int autofix)
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition: lwmpoint.c:45
void * lwalloc(size_t size)
Definition: lwutil.c:229
int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members) ...
Definition: lwgeom.c:1392
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
Here is the call graph for this function:
Here is the caller graph for this function: