10

I'm looking for a way to add a buffer around a PostGIS geometry, but the size of the buffer should depend on size of the geometry. That is, I want each geometry to be enlargened by, say, 5%.

The idea is I'm looking for intersecting geometries, but there could be an error of up to 5% associated with each one which I want to take into account.

Anyone know the best way of going about this?

The database has almsot a million rows, so I'd prefer it to be fairly fast.

2
  • 2
    5% of what? Presuming you're using polygons, is it 5% of the greatest width, the narrowest width, the bounding box, the distance of a vertex from the centroid...? If you're talking about points or lines, then it makes even less sense! Commented Jul 19, 2012 at 13:20
  • I guess of the vertex-centroid distance - or possibly a 5% increase in area would also be okay. Increasing the bounding box is fine if the geometry is scaled to then fill that bounding box. All the geometries are closed polygons (the vast majority being quadrilaterals). Commented Jul 19, 2012 at 13:51

3 Answers 3

8

The comments suggest the 5% does not need to be attained with high accuracy. (If it does, it will take a long time to buffer a million polygons!) We can therefore invoke the Pizza Principle: linearly rescaling a 2D feature by a factor a rescales its area by a^2.

Here's how the reasoning goes:

  • When the shape is not too convoluted--especially if it's convex--then buffering produces a result comparable to rescaling the shape around a central point. (It is important to understand, though, that buffering is not ever equivalent to a rescaling for any shapes other than disks. For some concave shapes, a "buffer" computed via rescaling might actually not include parts of the original shape itself! Therefore ultimately we will compute a genuine buffer of the shape, but are only using this approximate equivalence as a heuristic to estimate how much to buffer by.)

  • If the buffered area is to be 5% greater, then the amount of rescaling therefore should be sqrt(1 + 5/100), which is close to 1.025: that is, we should want to expand the shape by 2.5% in all directions.

  • Equivalently, if we think of the shape as having a "diameter" (equal to a typical distance across), its radius should increase by 2.5%. That's equal to 2.5%/2 = 1.25% of the diameter.

  • We can estimate a typical diameter from the shape's bounding box. Use, say, an arithmetic or geometric mean of the box's side lengths.

This suggests the following workflow:

  1. Obtain the shape's bounding box.

  2. Let e be the average of the side lengths of the box.

  3. Buffer the shape by 1.25% of e; that is, by (5/100)/4 * e.

Because steps 1 and 2 require very little computation, this offers itself as one of the most expeditious possible solutions. As a check of accuracy, you can (of course) compute the areas of the buffered shapes and compare them to the original areas to see how closely they come to the desired 5% increase. Sometimes the buffered areas will be even more than 5% greater, but it should be rare that they are less, and it's impossible for them to be appreciably less.

Examples

As a check and illustration, let's consider some simple shapes.

  1. A disk of radius r has a bounding box with sides of length 2 r. Our formula computes e = (5/100)/4 * 2 * r = r / 40. The buffered shape obviously is a concentric disk of radius r + r / 40 = 1.025 r. The old area was pi * r ^2 while the new area is pi * (1.025 r)^2 = pi * 1.0506 * r ^2, which is 5.06% greater.

  2. A rectangle with sides parallel to the coordinates axes of lengths r and s gives e = (r + s)/2. The additional area from buffering the rectangle comes from four rectangles of width (5/100)/4 e = e / 80 = (r + s)/160 bordering the sides plus four quarter-circles of radius e / 80 at the corners. Neglecting the quarter-circles, which will be small compared to the other areas, The total new area equals

    2(r + s) * (r + s)/160 = (r^2 + s^2 + 2 r * s) / 80.

    When r and s are not too different, we can figure r^2 + s^2 is approximately 2 r * s. This approximation simplifies the total new area to 4 r * s / 80 = 5% of the original area of r * s, as intended.

4

You want to use a combination of ST_Scale ( http://postgis.net/docs/ST_Scale.html) and ST_Translate ( http://postgis.net/docs/ST_Translate.html ) I think. We have an example of this in PostGIS in Action and similar in Chapter 8. If you don't have the book, you can download the code for that chapter here:

http://www.postgis.us/chapter_08

Snippet from book Look at example 8.26:

    -- Listing 8.26 Combining Scale and Translation to maintain centroid
    SELECT xfactor, yfactor, 
       ST_Translate(ST_Scale(hex.the_geom, xfactor, yfactor), 
       ST_X(ST_Centroid(the_geom))*(1 - xfactor), 
       ST_Y(ST_Centroid(the_geom))*(1 - yfactor) ) As scaled_geometry
    FROM 
 ( SELECT ST_GeomFromText('POLYGON((0 0,64 64,64 128,0 192,-64 128,-64 64,0 0))') As the_geom)  As hex
    CROSS JOIN (SELECT x*0.5 As xfactor 
        FROM generate_series(1,4) As x) As xf
    CROSS JOIN (SELECT y*0.5 As yfactor
        FROM generate_series(1,4) As y) As yf;
1
  • Works. Now there seems to be also ST_Transscale but somehow I can't figure how to make that work...
    – n1000
    Commented Apr 7, 2016 at 12:22
2

This is very late to the party but I recently developed a custom PostGIS function that does this, as well as shrinking it if necessary:

ST_Dilate

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.