# Measuring the similarity of two polygons in PostGIS

I want to measure the similarity of two polygons. I want to use the average distance of the boundaries as a measure of the shape similarity.

How can I do this in PostGIS?

• Hausdorff distance is not what you want? postgis.net/docs/ST_HausdorffDistance.html It measures of how similar or dissimilar 2 geometries are. – ThomasG77 May 21 at 20:40
• `ST_FrechetDistance` might be the better choice for comparing boundaries of equally ordered poygons. – geozelot May 21 at 21:33
• @geozelot Thanks for the mention to Fréchet distance (forgot it) but FYI, only for linestring (or misunderstood) whereas polygons here – ThomasG77 May 21 at 22:32
• @ThomasG77 no no, you're right about that; but depending on the preconditions (equal orientation, similar vertex location), comparing the actual boundaries this way should get more meaningful results. Btw. OP, I'd run a `[CROSS JOIN] LATERAL` with `ORDER BY ST_[Hausdorff|Frechet]Distance() LIMIT 1` in the subquery. – geozelot May 22 at 6:11

There are different ways to measure the similarity between two polygons such as average distance between the boundaries, Hausdorff distance, Turning Function, Comparing Fourier Transformation of the two polygons. Each method is suitable for one purpose.

As other users mentioned, the Hausdorff distance is a measure that shows the shape similarity. However, you should know that Hausdorff distance is the maximum distance possible between the boundaries of the two polygons. In fact if the two polygons match in 99% of their boundary but in 1% of the boundary they are 5m away from each other, the Hausdorff distance will be 5. Thus, it is a good measure to detect significant dissimilarities and it cannot tell you the average similarity between the two shapes.

Average distance on the other hand, is a average of the distances between the two boundaries and not the maximum of these distances. You can run the following code in pgAdmin to create a new function that calculates the average distance between the boundaries of two polygons.

``````DROP FUNCTION IF EXISTS average_distance;
CREATE OR REPLACE FUNCTION average_distance (poly1
geometry, poly2 geometry, sampling_dist DOUBLE
PRECISION)
RETURNS DOUBLE PRECISION AS \$total\$
DECLARE
avg_dist DOUBLE PRECISION;
BEGIN

WITH points AS(
SELECT (ST_DumpPoints(
ST_Segmentize(

ST_ExteriorRing(ST_Union(poly1))
,  sampling_dist
)
)
).geom

)

,poly2_corrected AS (
SELECT (ST_Union(poly2)) AS poly2_corr
)

,distances AS(
SELECT ST_Distance(ST_ExteriorRing((poly2_corr)),
geom) as dist
FROM points, poly2_corrected
)

SELECT AVG(dist)
FROM distances
INTO avg_dist;

RETURN avg_dist;
END;
\$total\$ LANGUAGE plpgsql;
``````

This function accepts two geometries and a sampling distance. It creates a set of points with the distance of "sampling distance" from each other on the boundary of the first geometry. Then, it measures the average distance between these points and the other geometry. Lets say that we want to calculate the average distance between the blue and brown polygons. First, this function creates the green points. Then, it calculates the average distance between the green points (samples of the boundary of the first geometry) and the blue geometry.

The signature of the function is:

``````DOUBLE PRECISION average_distance (poly1 geometry,
poly2 geometry, sampling_dist DOUBLE PRECISION)
``````

And you can call it like this:

``````SELECT average_distance (a.geom, b.geom, 100)
FROM polygons a , polygons b
``````

Where 100m is the distance between the green points.

Be careful, your geometries should be in a metric (projected) SRID. Otherwise, the output of the function wont be correct because the sampling distance is in meters. In addition, pay attention to the fact that average_distance (geom1, geom2) is not equal to average_distance (geom2, geom1) because when you choose your green sample points on the boundary of geom1 or geom2 their distance to the other geometry wont be the same! If you want a very exact average distance then, you should choose sample points on both geometries and then calculate the distance between the corresponding points. In this case, the challenge is finding the corresponding points!!! I do not like to open that door!

I used this function to measure the shape similarity of the polygons on OpenStreetMap with the reference polygons. In this case, you should move (ST_Translate())one of the geometries so that the two geometries become concentric (to remove the impact of the displacement from average distance function). By removing the distance between the two centroids, the result of this function will indicate how similar are the two shapes.

• Pedantic note: `ST_Segmentize` works on units of CRS. You could use percentage fractions or point count instead, and get the actual segmentize length by dividing by boundary length. No need to worry about units that way. – geozelot May 22 at 22:00
• Yes, you are right – milad May 24 at 3:57