9

I have a polygon shapefile and need to create new polygons, within the originals, as % of original area.

I used buffer by field (Field= new area as %) but the buffers are created in circles around the polygons.

What am I doing wrong?

I am using ArcGIS Desktop 10.

6 Answers 6

4

I suggest that you use the Buffer by percentage plugin available for QGIS.

It's not a solution using ArcMap but it does exactly what you want in a very efficient way.

3

you should use inner buffer.

  1. Select your layer with clicking on it
  2. From Editor toolbar, select Start Editing
  3. in Editor Menu, select Buffer
  4. Write Negative Distance Amount for inner buffer...

inner buffer

i hope it helps you...

5
  • Thanks for your reply but its not wha t i want. This way creates a buffer based on a linear distance from the edge lines of the poygon. what i want is to create a buffer, inside the original polygon, that will have area of eg. 75% or original polygon.
    – A_Y
    Commented Oct 2, 2013 at 10:53
  • did u write negative distance amount?
    – urcm
    Commented Oct 2, 2013 at 11:24
  • 1
    @user22538, This seems like a good solution. Are you trying to get something like the green polygon, or the tan stippled polygon, or something entirely different? Commented Oct 2, 2013 at 20:29
  • Something Like the green polygon but its area has to be 75% of the BLUE OUTLINE..buffer by negative distance doesn't give that area..
    – A_Y
    Commented Oct 3, 2013 at 7:52
  • The buffer solution must be iterative -- you need to try an initial value, then measure its area; if it's close enough, you're done, otherwise if it's too large, try a larger distance, and if it's too small, try a smaller distance. You can bounce between the smallest too-large and largest too-small until it's close enough.
    – Vince
    Commented Oct 11, 2013 at 15:54
3

The buffer command only works with linear distance from the edges, so to use the buffer command you'd need to try multiple negative buffer distances for each polygon until the area of the resulting feature was "correct enough". This could work, but would be computationally expensive (10-20, or maybe even 100-200 buffers per shape, depending on the size of your correctness threshold), and it wouldn't scale the inner rings (holes) in proportion to the exterior rings (outside).

Another option is to, for each polygon, extract all the coordinates, calculate a center-of-mass location, convert all the coordinates into polar notation (bearing and distance from the center), change all the distances to 75% of the original, then convert back to x,y notation, and write the resulting shape. This would even work for complex polygons with inner rings, if your center-of-mass was calculated only from the exterior ring, but with multi-part shapes you'd need to repeat the procedure with each exterior ring (and its respective inner rings). The drawback would be the need to do computational geometry on the coordinates (square root of sum of squares and atan2 of y2-y1 and x2-x1 for each coordinate pair).

2
  • From the centerpoints one could also buffer (the points) the relevant distance (trigonometry to calculate) to create a polygon with desired area, which then will be circular. However, it will require some hands-on work for each polygon.
    – Martin
    Commented Oct 2, 2013 at 11:38
  • Less difficult, but the shape of the original polygon would not be retained (and therefore not sure to be "within the originals", though scaling holes would produce similar issues).
    – Vince
    Commented Oct 2, 2013 at 11:44
2

I can only suggest a simple method that will give an approximate on average; yet if you are looking for the exact percentage reduction of area this would not work - but might guide your thoughts towards a coding solution. This method is based, like others have suggested, on negative input to the buffer tool. However it also involves adjustment to the the distance field inputs based on the "shape" of each polygon.

I suggest to use the compactness of the polygons to adjust the "reduction coefficient". More on compactness here.

To start with, I assume that your polygons are quite regular in shape, i.e. complete pseudo geometric shapes. Otherwise this might not work.

I started with this layer: StartWith

I computed the compactness using this formula:

4*3.14*[SHAPE_Area]/( [SHAPE_Length]^2)

Than, aiming for a-20% reduction in area, I computed the "new radius" (assuming a perfect circle) and adjusted the result using the compactness coefficient by multiplication. That is since a perfect circle's compactness index will be 1 (fully compact) and then the index decreases when the shpae is less compact. Thus a smaller "radius" is needed to get the same area.

The formula I used is:

-(0.2*Sqr ([SHAPE_Area])/3.14)* [Compact]

My results are presented in a scatter plot (between the new and original area) and summary statistics of the ratios between the new area to the original (hoping to get 0.8). Some deviation can be seen, yet I didn't lookup too carfully for their causes. It might be that few adjustments to the "compactness coefficient" will give better results.

ScatterPlot

And the stats: Stats

1

You can try "Convert Features to Graphics" -> select the polygons you are interested in -> right click properties -> "Size and Position". Check "As Percentage" and enter 75%

1
  • Good Idea, much faster and easier..but still new polygon area is less than 75% of original..i will use that idea for other tasks though..Thanks
    – A_Y
    Commented Oct 3, 2013 at 8:04
1

I have modified the QGIS version of the Buffer by percentage script in https://github.com/jdugge/BufferByPercentage/blob/master/bufferbypercentage.py and all credits goes to Juernjakob Dugge.

The process is quite simple.

  1. Create a new float field, say scale_factor and input the scale factor (this could be the same for all or the ratio to reach a certain/target area), say your polygon is 728363 sqm and you want to shrink it to 125967 sqm, in this case this factor is 17.294%. The input factor should be given as fraction, say 17.294% should be 0.17294.
  2. Create another double field, say buffer_length and use the field calculator in Python parser to input the expression and code below, where applicable:

Pre-Logic Script Code

def find_buffer_length(geometry, target_factor):
    """Find the buffer length that scales a geometry by a certain factor."""
    area_unscaled = geometry.area
    buffer_initial = 0.1 * (geometry.extent.width +
                            geometry.extent.height)

    buffer_length = secant(calculateError, buffer_initial,
                           2 * buffer_initial, geometry, 
                           area_unscaled, target_factor)

    return buffer_length


def calculateError(buffer_length, geometry, area_unscaled,
                   target_factor):
    """Calculate the difference between the current and the target factor."""
    geometry_scaled = geometry.buffer(buffer_length)
    area_scaled = geometry_scaled.area

    return area_scaled / area_unscaled - target_factor


# Secant method for iteratively finding the root of a function
# Taken from
# http://www.physics.rutgers.edu/~masud/computing/WPark_recipes_in_python.html
def secant(func, oldx, x, *args, **kwargs):
    """Find the root of a function"""
    tolerance = kwargs.pop('tolerance', 1e-6)
    max_steps = kwargs.pop('max_steps', 100)

    steps = 0
    oldf, f = func(oldx, *args), func(x, *args)

    if (abs(f) > abs(oldf)):  # Determine the initial search direction
        oldx, x = x, oldx
        oldf, f = f, oldf

    while (f - oldf) != 0 and steps < max_steps:
        dx = f * (x - oldx) / float(f - oldf)

        if abs(dx) < tolerance * (1 + abs(x)):  # Converged
            return x - dx

        oldx, x = x, x - dx
        oldf, f = f, func(x, *args)
        while f <= 0:
            # Buffer length resulted in flipped polygon, reduce step size
            x = oldx  # Undo current step
            f = oldf
            dx *= 0.5  # Halve the step size
            oldx, x = x, x - dx
            oldf, f = f, func(x, *args)

        steps += 1

    # Did not converge
    return x - dx

Field to be calculated (buffer_length =)

find_buffer_length( !SHAPE!, !scaling_factor! )

  1. Buffer the features based on the lastly calculated field.

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.