PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches

◆ lwpoly_to_points()

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

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

1503{
1504 double area, bbox_area, bbox_width, bbox_height;
1505 GBOX bbox;
1506 const LWGEOM* lwgeom = (LWGEOM*)lwpoly;
1507 uint32_t sample_npoints, sample_sqrt, sample_width, sample_height;
1508 double sample_cell_size;
1509 uint32_t i, j, n;
1510 uint32_t iterations = 0;
1511 uint32_t npoints_generated = 0;
1512 uint32_t npoints_tested = 0;
1513 GEOSGeometry* g;
1514 const GEOSPreparedGeometry* gprep;
1515 GEOSGeometry* gpt;
1516 GEOSCoordSequence* gseq;
1517 LWMPOINT* mpt;
1518 int32_t srid = lwgeom_get_srid(lwgeom);
1519 int done = 0;
1520 int* cells;
1521 const size_t size = 2 * sizeof(int);
1522 char tmp[2 * sizeof(int)];
1523 const size_t stride = 2 * sizeof(int);
1524
1525 if (lwgeom_get_type(lwgeom) != POLYGONTYPE)
1526 {
1527 lwerror("%s: only polygons supported", __func__);
1528 return NULL;
1529 }
1530
1531 if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL;
1532
1533 if (!lwpoly->bbox)
1534 lwgeom_calculate_gbox(lwgeom, &bbox);
1535 else
1536 bbox = *(lwpoly->bbox);
1537
1538 area = lwpoly_area(lwpoly);
1539 bbox_width = bbox.xmax - bbox.xmin;
1540 bbox_height = bbox.ymax - bbox.ymin;
1541 bbox_area = bbox_width * bbox_height;
1542
1543 if (area == 0.0 || bbox_area == 0.0)
1544 {
1545 lwerror("%s: zero area input polygon, TBD", __func__);
1546 return NULL;
1547 }
1548
1549 /* Gross up our test set a bit to increase odds of getting coverage in one pass */
1550 sample_npoints = npoints * bbox_area / area;
1551
1552 /* We're going to generate points using a sample grid as described
1553 * http://lin-ear-th-inking.blogspot.ca/2010/05/more-random-points-in-jts.html to try and get a more uniform
1554 * "random" set of points. So we have to figure out how to stick a grid into our box */
1555 sample_sqrt = lround(sqrt(sample_npoints));
1556 if (sample_sqrt == 0)
1557 sample_sqrt = 1;
1558
1559 /* Calculate the grids we're going to randomize within */
1560 if (bbox_width > bbox_height)
1561 {
1562 sample_width = sample_sqrt;
1563 sample_height = ceil((double)sample_npoints / (double)sample_width);
1564 sample_cell_size = bbox_width / sample_width;
1565 }
1566 else
1567 {
1568 sample_height = sample_sqrt;
1569 sample_width = ceil((double)sample_npoints / (double)sample_height);
1570 sample_cell_size = bbox_height / sample_height;
1571 }
1572
1573 /* Prepare the polygon for fast true/false testing */
1574 initGEOS(lwnotice, lwgeom_geos_error);
1575 g = (GEOSGeometry*)LWGEOM2GEOS(lwgeom, 0);
1576 if (!g)
1577 {
1578 lwerror("%s: Geometry could not be converted to GEOS: %s", __func__, lwgeom_geos_errmsg);
1579 return NULL;
1580 }
1581 gprep = GEOSPrepare(g);
1582
1583 /* Get an empty multi-point ready to return */
1584 mpt = lwmpoint_construct_empty(srid, 0, 0);
1585
1586 /* Initiate random number generator.
1587 * Repeatable numbers are generated with seed values >= 1.
1588 * When seed is zero and has not previously been set, it is based on
1589 * Unix time (seconds) and process ID. */
1590 lwrandom_set_seed(seed);
1591
1592 /* Now we fill in an array of cells, and then shuffle that array, */
1593 /* so we can visit the cells in random order to avoid visual ugliness */
1594 /* caused by visiting them sequentially */
1595 cells = lwalloc(2 * sizeof(int) * sample_height * sample_width);
1596 for (i = 0; i < sample_width; i++)
1597 {
1598 for (j = 0; j < sample_height; j++)
1599 {
1600 cells[2 * (i * sample_height + j)] = i;
1601 cells[2 * (i * sample_height + j) + 1] = j;
1602 }
1603 }
1604
1605 /* Fisher-Yates shuffle */
1606 n = sample_height * sample_width;
1607 if (n > 1)
1608 {
1609 for (i = n - 1; i > 0; i--)
1610 {
1611 size_t j = (size_t)(lwrandom_uniform() * (i + 1));
1612
1613 memcpy(tmp, (char *)cells + j * stride, size);
1614 memcpy((char *)cells + j * stride, (char *)cells + i * stride, size);
1615 memcpy((char *)cells + i * stride, tmp, size);
1616 }
1617 }
1618
1619 /* Start testing points */
1620 while (npoints_generated < npoints)
1621 {
1622 iterations++;
1623 for (i = 0; i < sample_width * sample_height; i++)
1624 {
1625 int contains = 0;
1626 double y = bbox.ymin + cells[2 * i] * sample_cell_size;
1627 double x = bbox.xmin + cells[2 * i + 1] * sample_cell_size;
1628 x += lwrandom_uniform() * sample_cell_size;
1629 y += lwrandom_uniform() * sample_cell_size;
1630 if (x >= bbox.xmax || y >= bbox.ymax) continue;
1631
1632 gseq = GEOSCoordSeq_create(1, 2);
1633#if POSTGIS_GEOS_VERSION < 38
1634 GEOSCoordSeq_setX(gseq, 0, x);
1635 GEOSCoordSeq_setY(gseq, 0, y);
1636#else
1637 GEOSCoordSeq_setXY(gseq, 0, x, y);
1638#endif
1639 gpt = GEOSGeom_createPoint(gseq);
1640
1641 contains = GEOSPreparedIntersects(gprep, gpt);
1642
1643 GEOSGeom_destroy(gpt);
1644
1645 if (contains == 2)
1646 {
1647 GEOSPreparedGeom_destroy(gprep);
1648 GEOSGeom_destroy(g);
1649 lwerror("%s: GEOS exception on PreparedContains: %s", __func__, lwgeom_geos_errmsg);
1650 return NULL;
1651 }
1652 if (contains)
1653 {
1654 npoints_generated++;
1655 mpt = lwmpoint_add_lwpoint(mpt, lwpoint_make2d(srid, x, y));
1656 if (npoints_generated == npoints)
1657 {
1658 done = 1;
1659 break;
1660 }
1661 }
1662
1663 /* Short-circuit check for ctrl-c occasionally */
1664 npoints_tested++;
1665 if (npoints_tested % 10000 == 0)
1666 LW_ON_INTERRUPT(GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); return NULL);
1667
1668 if (done) break;
1669 }
1670 if (done || iterations > 100) break;
1671 }
1672
1673 GEOSPreparedGeom_destroy(gprep);
1674 GEOSGeom_destroy(g);
1675 lwfree(cells);
1676
1677 return mpt;
1678}
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:909
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition lwmpoint.c:45
void * lwalloc(size_t size)
Definition lwutil.c:227
void lwfree(void *mem)
Definition lwutil.c:242
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition lwpoint.c:163
#define POLYGONTYPE
Definition liblwgeom.h:118
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:737
#define LW_ON_INTERRUPT(x)
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:135
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:193
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:343
double xmax
Definition liblwgeom.h:341
double ymin
Definition liblwgeom.h:342
double xmin
Definition liblwgeom.h:340
GBOX * bbox
Definition liblwgeom.h:504

References 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(), lwrandom_set_seed(), lwrandom_uniform(), POLYGONTYPE, GBOX::xmax, GBOX::xmin, 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: