Necesito un algoritmo en el que pueda verificar si un punto cae dentro de un polígono dentro de una tolerancia pequeña (lo que significa que el punto está completamente dentro del polígono o lo suficientemente cerca del borde). Para implementar esto, estaba planeando usar la función st_scale
para expandir el polígono por un factor, luego verificar el punto con st_within
. Tenga en cuenta que no puedo usar st_buffer
porque st_buffer
espera un radio absoluto del búfer pero necesito un radio relativo (por ejemplo, determine si el punto está dentro del polígono o dentro del polígono que se ha ampliado en un 5%).
Para lograr esto, he escrito el siguiente código de prueba. Tomo un polígono, lo amplío en un 5 % y luego pruebo si el centroide del polígono original se encuentra dentro del polígono ampliado:
SELECT ST_within( st_centroid(polygon), st_scale( polygon, 'POINT(1.05 1.05)'::geometry, st_centroid(polygon) ));
Sin embargo, a veces esta función falla sin razón aparente (lo que significa que dice que el centroide del polígono no está dentro del polígono ampliado) y no tengo idea de por qué. Parece que tiene errores, pero no puedo imaginar que haya encontrado un error en postGIS.
Pasando casos de prueba:
-- drawing a 1x1 box at the origin of the xy plane SELECT ST_Within( st_centroid('MULTIPOLYGON(((0 0, 1 0, 1 1, 0 1, 0 0)))'::geometry), st_scale( 'MULTIPOLYGON(((0 0, 1 0, 1 1, 0 1, 0 0)))'::geometry, 'POINT(1.05 1.05)'::geometry, st_centroid('MULTIPOLYGON(((0 0, 1 0, 1 1, 0 1, 0 0)))'::geometry)) ); -- box in the first quadrant at an origin of (10, 10) SELECT ST_Within( st_centroid('MULTIPOLYGON(((10 10, 11 10, 11 11, 10 11, 10 10)))'::geometry), st_scale( 'MULTIPOLYGON(((10 10, 11 10, 11 11, 10 11, 10 10)))'::geometry, 'POINT(1.05 1.05)'::geometry, st_centroid('MULTIPOLYGON(((10 10, 11 10, 11 11, 10 11, 10 10)))'::geometry)) );
Caso de prueba fallido:
-- 1x1 box with the bottom left corner at (98, 28) SELECT ST_within( st_centroid('MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry), st_scale( 'MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry, 'POINT(1.05 1.05)'::geometry, st_centroid('MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry)) );
Curiosamente, sin embargo, convertir la forma escalada en WKT y luego volver a una geometría SÍ funciona.
-- 1x1 box with the bottom left corner at (98, 28) SELECT ST_within( st_centroid('MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry), st_geomfromewkt(st_asewkt(st_scale( 'MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry, 'POINT(1.05 1.05)'::geometry, st_centroid('MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry)))) );
Entonces, sí, ¿qué está pasando aquí???? ¿He encontrado un error en PostGIS?
Creo que has encontrado un error en versiones anteriores de postgis. Usé docker y algunos servidores para probar con varias versiones de postgres y postgis. Con la versión 3.1.4 el código funciona correctamente.
En el pasado, me he topado con algunos casos de esquina donde postgis dio resultados inesperados. Esas estaban relacionadas con GEOS
, que según el manual es la librería que implementa la función ST_Within
, pero en este caso la librería GEOS
es la misma en todas las pruebas.
Por lo que muestra su consulta de trabajo que se transforma en ewkt
y retrocede, esto debería estar relacionado con una operación interna en postgis que se corrigió. Lo intenté pero no encontré un error que pudiera relacionarme con esto.
Os dejo dos de las pruebas que he hecho:
El error está presente:
test=# select postgis_full_version(); postgis_full_version ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- POSTGIS="3.0.0 r17983" [EXTENSION] PGSQL="120" GEOS="3.7.1-CAPI-1.11.1 27a5e771" PROJ="Rel. 5.2.0, September 15th, 2018" LIBXML="2.9.4" LIBJSON="0.12.1" LIBPROTOBUF="1.3.1" WAGYU="0.4.3 (Internal)" TOPOLOGY (1 row) test=# SELECT ST_within( st_centroid('MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry), st_scale( 'MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry, 'POINT(1.05 1.05)'::geometry, st_centroid('MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry)) ); st_within ----------- f (1 row)
El código funciona como se esperaba:
test=# select postgis_full_version(); postgis_full_version ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- POSTGIS="3.1.4 ded6c34" [EXTENSION] PGSQL="130" GEOS="3.7.1-CAPI-1.11.1 27a5e771" PROJ="Rel. 5.2.0, September 15th, 2018" LIBXML="2.9.4" LIBJSON="0.12.1" LIBPROTOBUF="1.3.1" WAGYU="0.5.0 (Internal)" TOPOLOGY (1 row) test=# SELECT ST_within( st_centroid('MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry), st_scale( 'MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry, 'POINT(1.05 1.05)'::geometry, st_centroid('MULTIPOLYGON(((98 28,99 28,99 29,98 29,98 28)))'::geometry)) ); st_within ----------- t (1 row)