PostGIS  3.7.0dev-r@@SVN_REVISION@@

◆ lwpoly_to_points()

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

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

1416 {
1417 
1418  typedef struct CellCorner {
1419  double x;
1420  double y;
1421  } CellCorner;
1422 
1423  CellCorner* cells;
1424  uint32_t num_cells = 0;
1425 
1426  double area, bbox_area, bbox_width, bbox_height, area_ratio;
1427  GBOX bbox;
1428  const LWGEOM* lwgeom = (LWGEOM*)lwpoly;
1429  uint32_t sample_npoints, sample_sqrt, sample_width, sample_height;
1430  double sample_cell_size;
1431  uint32_t i, j, n;
1432  uint32_t iterations = 0;
1433  uint32_t npoints_generated = 0;
1434  uint32_t npoints_tested = 0;
1435  GEOSGeometry* g;
1436  const GEOSPreparedGeometry* gprep;
1437  LWMPOINT* mpt;
1438  int32_t srid = lwgeom_get_srid(lwgeom);
1439  int done = 0;
1440 
1441  if (lwgeom_get_type(lwgeom) != POLYGONTYPE)
1442  {
1443  lwerror("%s: only polygons supported", __func__);
1444  return NULL;
1445  }
1446 
1447  if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL;
1448 
1449  if (!lwpoly->bbox)
1450  lwgeom_calculate_gbox(lwgeom, &bbox);
1451  else
1452  bbox = *(lwpoly->bbox);
1453 
1454  area = lwpoly_area(lwpoly);
1455  bbox_width = bbox.xmax - bbox.xmin;
1456  bbox_height = bbox.ymax - bbox.ymin;
1457  bbox_area = bbox_width * bbox_height;
1458 
1459  if (area == 0.0 || bbox_area == 0.0)
1460  {
1461  lwerror("%s: zero area input polygon, TBD", __func__);
1462  return NULL;
1463  }
1464 
1465  /* Gross up our test set a bit to increase odds of getting coverage
1466  * in one pass. For narrow inputs, it is possible we will get
1467  * no hits at all. A fall-back approach might use a random stab-line
1468  * to find pairs of edges that bound interior area, and generate a point
1469  * between those edges, in the case that this gridding system
1470  * does not generate the desired number of points */
1471  area_ratio = FP_MIN(bbox_area / area, 10000.0);
1472  sample_npoints = npoints * area_ratio;
1473 
1474  /* We're going to generate points using a sample grid as described
1475  * http://lin-ear-th-inking.blogspot.ca/2010/05/more-random-points-in-jts.html
1476  * to try and get a more uniform "random" set of points. So we have to figure
1477  * out how to stick a grid into our box */
1478  sample_sqrt = ceil(sqrt(sample_npoints));
1479 
1480  /* Calculate the grids we're going to randomize within */
1481  if (bbox_width > bbox_height)
1482  {
1483  sample_width = sample_sqrt;
1484  sample_height = ceil((double)sample_npoints / (double)sample_width);
1485  sample_cell_size = bbox_width / sample_width;
1486  }
1487  else
1488  {
1489  sample_height = sample_sqrt;
1490  sample_width = ceil((double)sample_npoints / (double)sample_height);
1491  sample_cell_size = bbox_height / sample_height;
1492  }
1493 
1494  /* Prepare the polygon for fast true/false testing */
1495  initGEOS(lwnotice, lwgeom_geos_error);
1496  g = (GEOSGeometry*)LWGEOM2GEOS(lwgeom, 0);
1497  if (!g)
1498  {
1499  lwerror("%s: Geometry could not be converted to GEOS: %s", __func__, lwgeom_geos_errmsg);
1500  return NULL;
1501  }
1502  gprep = GEOSPrepare(g);
1503 
1504  /* START_PROFILING(); */
1505 
1506  /* Now we fill in an array of cells, only using cells that actually
1507  * intersect the geometry of interest, and finally weshuffle that array,
1508  * so we can visit the cells in random order to avoid visual ugliness
1509  * caused by visiting them sequentially */
1510  cells = lwalloc(sizeof(CellCorner) * sample_height * sample_width);
1511 
1512  for (i = 0; i < sample_width; i++)
1513  {
1514  for (j = 0; j < sample_height; j++)
1515  {
1516  int intersects = 0;
1517  double xmin = bbox.xmin + i * sample_cell_size;
1518  double ymin = bbox.ymin + j * sample_cell_size;
1519  GEOSGeometry *gcell = lwpoly_to_points_make_cell(xmin, ymin, sample_cell_size);
1520  intersects = GEOSPreparedIntersects(gprep, gcell);
1521  GEOSGeom_destroy(gcell);
1522  if (intersects == 2)
1523  {
1524  GEOSPreparedGeom_destroy(gprep);
1525  GEOSGeom_destroy(g);
1526  lwerror("%s: GEOS exception on GEOSPreparedIntersects: %s", __func__, lwgeom_geos_errmsg);
1527  return NULL;
1528  }
1529  if (intersects == 1)
1530  {
1531  cells[num_cells].x = xmin;
1532  cells[num_cells].y = ymin;
1533  num_cells++;
1534  }
1535  }
1536  }
1537 
1538  /* STOP_PROFILING(CellGeneration); */
1539 
1540  /* Initiate random number generator.
1541  * Repeatable numbers are generated with seed values >= 1.
1542  * When seed is zero and has not previously been set, it is based on
1543  * Unix time (seconds) and process ID. */
1544  lwrandom_set_seed(seed);
1545 
1546  /* Fisher-Yates shuffle */
1547  n = num_cells;
1548  if (n > 1)
1549  {
1550  for (i = n - 1; i > 0; i--)
1551  {
1552  CellCorner tmp;
1553  j = (uint32_t)(lwrandom_uniform() * (i + 1));
1554  memcpy( &tmp, cells + j, sizeof(CellCorner));
1555  memcpy(cells + j, cells + i, sizeof(CellCorner));
1556  memcpy(cells + i, &tmp, sizeof(CellCorner));
1557  }
1558  }
1559 
1560  /* Get an empty array of points ready to return */
1561  mpt = lwmpoint_construct_empty(srid, 0, 0);
1562 
1563  /* Start testing points */
1564  while (npoints_generated < npoints)
1565  {
1566  iterations++;
1567  for (i = 0; i < num_cells; i++)
1568  {
1569  int contains = 0;
1570  double y = cells[i].y;
1571  double x = cells[i].x;
1572  x += lwrandom_uniform() * sample_cell_size;
1573  y += lwrandom_uniform() * sample_cell_size;
1574  if (x >= bbox.xmax || y >= bbox.ymax) continue;
1575 
1576 #if POSTGIS_GEOS_VERSION >= 31200
1577  contains = GEOSPreparedIntersectsXY(gprep, x, y);
1578 #else
1579  {
1580  GEOSGeometry *gpt;
1581  GEOSCoordSequence *gseq = GEOSCoordSeq_create(1, 2);;
1582  GEOSCoordSeq_setXY(gseq, 0, x, y);
1583  gpt = GEOSGeom_createPoint(gseq);
1584  contains = GEOSPreparedIntersects(gprep, gpt);
1585  GEOSGeom_destroy(gpt);
1586  }
1587 #endif
1588  if (contains == 2)
1589  {
1590  GEOSPreparedGeom_destroy(gprep);
1591  GEOSGeom_destroy(g);
1592  lwerror("%s: GEOS exception on GEOSPreparedIntersects: %s", __func__, lwgeom_geos_errmsg);
1593  return NULL;
1594  }
1595  if (contains == 1)
1596  {
1597  mpt = lwmpoint_add_lwpoint(mpt, lwpoint_make2d(srid, x, y));
1598  npoints_generated++;
1599  if (npoints_generated == npoints)
1600  {
1601  done = 1;
1602  break;
1603  }
1604  }
1605 
1606  /* Short-circuit check for ctrl-c occasionally */
1607  npoints_tested++;
1608  if (npoints_tested % 10000 == 0)
1609  LW_ON_INTERRUPT(GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); return NULL);
1610 
1611  if (done) break;
1612  }
1613  if (done || iterations > 100) break;
1614  }
1615 
1616  GEOSPreparedGeom_destroy(gprep);
1617  GEOSGeom_destroy(g);
1618  lwfree(cells);
1619 
1620  return mpt;
1621 }
static GEOSGeometry * lwpoly_to_points_make_cell(double xmin, double ymin, double cell_size)
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:248
#define POLYGONTYPE
Definition: liblwgeom.h:104
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
Datum contains(PG_FUNCTION_ARGS)
void lwnotice(const char *fmt,...) __attribute__((format(printf
Write a notice out to the notice handler.
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:141
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:199
void lwrandom_set_seed(int32_t seed)
Definition: lwrandom.c:48
double lwrandom_uniform(void)
Definition: lwrandom.c:94
double ymax
Definition: liblwgeom.h:357
double xmax
Definition: liblwgeom.h:355
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354

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(), lwpoly_to_points_make_cell(), 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: