PostGIS 3.7.0dev-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 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}
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
static GEOSGeometry * lwpoly_to_points_make_cell(double xmin, double ymin, double cell_size)
void lwgeom_geos_error(const char *fmt,...)
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition lwgeom.c:955
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:248
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition lwpoint.c:163
#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:783
#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:433
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
GBOX * bbox
Definition liblwgeom.h:518

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, 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: