Skip to main content
Solved

Calculating area of overlap for one feature layer against another, and filtering based on percentage of overlap.


nigelgriffiths
Contributor
Forum|alt.badge.img+1

Hello, I am attempting to replicate the following PostGIS SQL code within an FME workspace:
 

where ST_DWithin(property_boundary.shape ,restriction_area."SP_GEOMETRY",100) and ST_AREA( ST_INTERSECTION(property_boundary.shape ,restriction_area."SP_GEOMETRY"))/ST_AREA(restriction_area."SP_GEOMETRY") < 0.1

 

property_boundary layer is parcel data for an area. restriction_area is a collection of polygons where the same restriction applies but for different reasons which is listed in the attributes. If the above script is true for a given property an attribute is then applied.

 

I am not very familar with PostGIS SQL, but from my understanding, this script is filtering for all property_boundary shapes that intersect a 100 meter buffer of the restriction_area. Then, it calculates the total overlap between each property_boundary shape, and each restriction_area shape. If the area of overlap for a property_boundary shape is less than 10% of the total for a given restriction_area it is selected in the where clause (and attribute then applied).

 

The problem I am having with using the AreaOnAreaOverlayer is that there is some very small self-intersection between neighbouring property_boundary features, with small slivers and boundary lines being output, thus I am getting many more features in the output than I should.

 

Referencing the above SQL script, how could I best replicate this in FME.

 

Best answer by bwn

There are quite a few strategies for using AreaOnAreaOverlayer to test the overlap of A vs B.  One more common method I would use is to first find an A.ID vs B.ID table using NeighborFinder, and then restrict AreaOnAreaOverlayer to just these pairs using the Group By Parameter.  This will stop it attempting to test geometries in Table A against other geometries in Table A and creating Eg. thousands….millions etc. of slivers.

 

However, I’m going to put a left-field solution up though which is a Spatial SQL based solution that can be high-performing and maybe easier sometimes to translate existing SQL into FME.

What we can do is create a temporary Spatialite Database in-workspace, and run SQL against this.  The first bit is a bit clunky, in needing to generate a temporary FME file name and joining that to the records to use FeatureWriter to create a temporary Spatialite Database, but after that we can run SQL against this to mimic the original PostGIS syntax.

The ST_DWithin() part of the clause is a bit funny, in that the second part of the clause that uses ST_Intersection() will only evaluate to True if Distance is = 0………..not 100 metres.  So instead below I’ve just used ST_Intersects() as the test rather than ST_DWithin().

 

Within the SQLExecutor then is a replica of the original PostGIS SQL, although as above, used ST_Intersects() rather than ST_DWithin().  I think the original author was worried about coordinate rounding errors for triggering the spatial index so was using ST_DWithin() as a kind of buffer to make sure the spatial index in PostGIS picked up all possible intersecting candidates, but in Spatialite, its spatial index already has a degree of buffering in it to handle these edge cases so we don’t need to use that strategy.

SELECT A.A_ID, B.B_ID
FROM A,B
WHERE ST_Intersects(A.geometry,B.geometry)
AND ST_Area(ST_Intersection(A.geometry, B.geometry))/ST_Area(B.geometry)<0.1

--Queries in Spatialite do not automatically make use of the Spatial R*Tree Indices when call Eg. ST_Intersects()
--They must be manually used within the query to optimise query speed
--Spatialite uses a programmatic wrapper object called "SpatialIndex" that allows easier search
--through the separate spatial index tables.

--To limit which rows of A will be compared with which rows from B
--Specify the name of the table to search its spatial index "f_table_name"
--and specify what geometry to test if it interesects the spatial index row within "search_frame"

--The best way is to use the table that has the most geometries that already have their Minimum Bounding Rectangles (MBR)
--Pre-indexed, which is Table A.
--We will ask which rows from A have Minimum Bounding Rectangles that interesect with the Minimum Boundaring Rectangles of B
--which is what happens by specifying B geometries as the Search Frame.  Spatialite will convert the more complex B geometries
--to an MBR Search Box "Frame"

--Spatial Index lookup then takes the form of, for any Row of A being tested in the WHERE, check if its spatially indexed MBR is
--is intersected by any MBR of B.  Note that can use any geometry expression "search_frame" including buffering to get an equivalent
--"Within a certain distance" spatial index search.  Eg. search_frame = ST_Buffer(ST_Envelope(B.Geometry),100) would create an enveloping
--box from the B geometries to turn a complex geometry into a simple one, and then buffer this box by 100 distance units
--This is the equivalent of using a spatial index to check if Within 100 metres

AND A.ROWID IN (
    SELECT ROWID 
    FROM SpatialIndex
    WHERE f_table_name = 'A' 
        AND search_frame = B.geometry)

 

View original
Did this help you find an answer to your question?

2 replies

liamfez
Influencer
Forum|alt.badge.img+34
  • Influencer
  • October 9, 2024

For your little slivers that are causing larger numbers of output features than input, I would recommend trying either the StatisticsCalculator or the Aggregator. Assuming you have some ID for each area/polygon (if not you can use a Counter to create one before you do the AreaOnArea or any clipping), then you can group by that polygon ID and sum the area measurements.

There are a couple different ways you can do this, but here is one general method:

  1. Calculate area of polygons
  2. Clip polygons using AreaOnArea, Clipper, etc
  3. Calculate area of resulting polygons
  4. Aggregator or StatisticsCalculator to sum areas of all the slivers
  5. Compare summed overlap areas against initial areas

bwn
Evangelist
Forum|alt.badge.img+26
  • Evangelist
  • Best Answer
  • October 9, 2024

There are quite a few strategies for using AreaOnAreaOverlayer to test the overlap of A vs B.  One more common method I would use is to first find an A.ID vs B.ID table using NeighborFinder, and then restrict AreaOnAreaOverlayer to just these pairs using the Group By Parameter.  This will stop it attempting to test geometries in Table A against other geometries in Table A and creating Eg. thousands….millions etc. of slivers.

 

However, I’m going to put a left-field solution up though which is a Spatial SQL based solution that can be high-performing and maybe easier sometimes to translate existing SQL into FME.

What we can do is create a temporary Spatialite Database in-workspace, and run SQL against this.  The first bit is a bit clunky, in needing to generate a temporary FME file name and joining that to the records to use FeatureWriter to create a temporary Spatialite Database, but after that we can run SQL against this to mimic the original PostGIS syntax.

The ST_DWithin() part of the clause is a bit funny, in that the second part of the clause that uses ST_Intersection() will only evaluate to True if Distance is = 0………..not 100 metres.  So instead below I’ve just used ST_Intersects() as the test rather than ST_DWithin().

 

Within the SQLExecutor then is a replica of the original PostGIS SQL, although as above, used ST_Intersects() rather than ST_DWithin().  I think the original author was worried about coordinate rounding errors for triggering the spatial index so was using ST_DWithin() as a kind of buffer to make sure the spatial index in PostGIS picked up all possible intersecting candidates, but in Spatialite, its spatial index already has a degree of buffering in it to handle these edge cases so we don’t need to use that strategy.

SELECT A.A_ID, B.B_ID
FROM A,B
WHERE ST_Intersects(A.geometry,B.geometry)
AND ST_Area(ST_Intersection(A.geometry, B.geometry))/ST_Area(B.geometry)<0.1

--Queries in Spatialite do not automatically make use of the Spatial R*Tree Indices when call Eg. ST_Intersects()
--They must be manually used within the query to optimise query speed
--Spatialite uses a programmatic wrapper object called "SpatialIndex" that allows easier search
--through the separate spatial index tables.

--To limit which rows of A will be compared with which rows from B
--Specify the name of the table to search its spatial index "f_table_name"
--and specify what geometry to test if it interesects the spatial index row within "search_frame"

--The best way is to use the table that has the most geometries that already have their Minimum Bounding Rectangles (MBR)
--Pre-indexed, which is Table A.
--We will ask which rows from A have Minimum Bounding Rectangles that interesect with the Minimum Boundaring Rectangles of B
--which is what happens by specifying B geometries as the Search Frame.  Spatialite will convert the more complex B geometries
--to an MBR Search Box "Frame"

--Spatial Index lookup then takes the form of, for any Row of A being tested in the WHERE, check if its spatially indexed MBR is
--is intersected by any MBR of B.  Note that can use any geometry expression "search_frame" including buffering to get an equivalent
--"Within a certain distance" spatial index search.  Eg. search_frame = ST_Buffer(ST_Envelope(B.Geometry),100) would create an enveloping
--box from the B geometries to turn a complex geometry into a simple one, and then buffer this box by 100 distance units
--This is the equivalent of using a spatial index to check if Within 100 metres

AND A.ROWID IN (
    SELECT ROWID 
    FROM SpatialIndex
    WHERE f_table_name = 'A' 
        AND search_frame = B.geometry)

 


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings