I'm finally asking a question for this because it's been days I'm on it!
My problem is the following one : In my database, I have a lot of polygons which are in the same region. I'm trying to create a query for 3 polygons which let me know all possibilities where 3 polygons are neighbour AND the sum of their 3 areas is superior to 41 and inferior to 43 (example)
I already got it for 2 polygons but all my tries for 3 polygons have been a failure, I can get a result but I have to wait way too long (a few hours sometimes ). However, with 2 polygons it can be a few seconds ... I'm running my script on my server (12 core and 64 RAM)
Here is the code I was using for 3 polygons search ...
SELECT DISTINCT
p1.id As p1_id, p2.id As p2_id, p3.id As p3_id
FROM table1 As p1, table1 As p2, table1 As p3
WHERE
p1.region_id IN (17)
AND p2.region_id IN (17)
AND p3.region_id IN (17)
AND p1.id <> p2.id
AND p1.id <> p3.id
AND p2.id <> p3.id
AND ((ST_Area(p1.polygon) * 0.67 + ST_Area(p2.polygon) * 0.67 + ST_Area(p3.polygon) * 0.67) > 41.0)
AND ((ST_Area(p1.polygon) * 0.67 + ST_Area(p2.polygon) * 0.67 + ST_Area(p3.polygon) * 0.67) < 43.0)
AND (
(ST_DWithin(p1.polygon, p2.polygon, 1.0, true) AND ST_DWithin(p2.polygon, p3.polygon, 1.0, true))
OR (ST_DWithin(p1.polygon, p3.polygon, 1.0, true) AND ST_DWithin(p3.polygon, p2.polygon, 1.0, true))
OR (ST_DWithin(p2.polygon, p1.polygon, 1.0, true) AND ST_DWithin(p1.polygon, p3.polygon, 1.0, true))
)
LIMIT 10;
Any advice or help is welcome!
Another info: my goal was to be able to generate this kind of query for X number of polygons
Thank's and have a nice day
################################## Edit
As asked by Jim, here is an example
We suppose this region with 11 polygons:
We are looking for 3 polygons which are neighbour and sum area equal to 7 In this case, I suppose 3 solutions exist:
For the last one, it's only an intersect on the point, I'm not sure I will keep this result later, it will maybe 2 points at least to be considered (by this I'm avoiding angle ..)
P1 = POLYGON(( 0.0 0.0, 0.0 1.0, 1.0 1.0, 1.0 0.0 ))
P2 = POLYGON(( 1.0 0.0, 1.0 1.0, 3.0 1.0, 3.0 0.0 ))
P3 = POLYGON(( 3.0 0.0, 3.0 2.0, 4.0 2.0, 4.0 0.0 ))
P4 = POLYGON(( 0.0 1.0, 0.0 3.0, 1.0 3.0, 1.0 1.0 ))
P5 = POLYGON(( 1.0 1.0, 1.0 2.0, 2.0 2.0, 2.0 1.0 ))
P6 = POLYGON(( 2.0 1.0, 2.0 2.0, 3.0 2.0, 3.0 1.0 ))
P7 = POLYGON(( 1.0 2.0, 1.0 3.0, 2.0 3.0, 2.0 2.0 ))
P8 = POLYGON(( 2.0 2.0, 2.0 3.0, 3.0 3.0, 3.0 2.0 ))
P9 = POLYGON(( 0.0 3.0, 0.0 4.0, 1.0 4.0, 1.0 3.0 ))
P10 = POLYGON(( 1.0 3.0, 1.0 4.0, 3.0 4.0, 3.0 3.0 ))
P11 = POLYGON(( 3.0 2.0, 3.0 5.0, 4.0 5.0, 4.0 2.0 ))
So for a multipolygon:
MULTIPOLYGON((( 0.0 0.0, 0.0 1.0, 1.0 1.0, 1.0 0.0 )),
(( 1.0 0.0, 1.0 1.0, 3.0 1.0, 3.0 0.0 )),
(( 3.0 0.0, 3.0 2.0, 4.0 2.0, 4.0 0.0 )),
(( 0.0 1.0, 0.0 3.0, 1.0 3.0, 1.0 1.0 )),
(( 1.0 1.0, 1.0 2.0, 2.0 2.0, 2.0 1.0 )),
(( 2.0 1.0, 2.0 2.0, 3.0 2.0, 3.0 1.0 )),
(( 1.0 2.0, 1.0 3.0, 2.0 3.0, 2.0 2.0 )),
(( 2.0 2.0, 2.0 3.0, 3.0 3.0, 3.0 2.0 )),
(( 0.0 3.0, 0.0 4.0, 1.0 4.0, 1.0 3.0 )),
(( 1.0 3.0, 1.0 4.0, 3.0 4.0, 3.0 3.0 )),
(( 3.0 2.0, 3.0 5.0, 4.0 5.0, 4.0 2.0 )))
I hope I haven't made a mistake on coordinates. Thank's everyone for the differents answer :D
For 3, we can say that a central polygon is connected to 2 others. I would focus on finding this central polygon. It will eventually lead to duplicates (if the 3 polygons touch each others) that you would have to filter out.
Also make sure to have an index on st_area(geom)
or to pre-compute it. (In fact, a multi-column index on id, region, st_area(geom)
should be the most efficient)
Most importantly, have a spatial index.
SELECT
p1.id As p1_id, p2.id As p2_id, p3.id As p3_id
FROM table1 As p1
JOIN table1 As p2 ON ST_DWithin(p1.polygon, p2.polygon, 1.0, true)
JOIN table1 As p3 ON ST_DWithin(p1.polygon, p3.polygon, 1.0, true)
WHERE
p1.region_id IN (17)
AND p2.region_id IN (17)
AND p3.region_id IN (17)
AND p1.id <> p2.id
AND p1.id <> p3.id
AND p2.id <> p3.id
AND ((ST_Area(p1.polygon) * 0.67 + ST_Area(p2.polygon) * 0.67 + ST_Area(p3.polygon) * 0.67) > 41.0)
AND ((ST_Area(p1.polygon) * 0.67 + ST_Area(p2.polygon) * 0.67 + ST_Area(p3.polygon) * 0.67) < 43.0
LIMIT 10;