7

I use QGIS (2.10.1) to create buffers around polygons. I often create a series of buffers, and later need to edit the underlying polygons. It is frustrating to start from scratch in re-create several buffers based on the changes when this happens.

Is there a way to create a 'live' buffer, that will automatically update to changes made to the parent polygon, or as a second best, a means to replicate with one click, the buffer settings and formatting that you originally created?

9
  • 1
    Look forward to seeing other responses to this but the only success I have had is from storing the base data in a postGIS database and dynamically drawing buffers with a view using ST_Buffer. If you cannot use postgresql then your best bet might well be scripting the buffer creation so you can at least regenerate the buffer zones with minimal fuss. I'd use R but there's probably an easier way of scripting it in python within QGIS. – Tumbledown Nov 5 '15 at 14:48
  • Quite easy to do if you store your data in PostgreSQL/PostGIS. At least for creating selections, not as sure about actually displaying the buffers. – HeikkiVesanto Nov 5 '15 at 15:01
  • Also checkout the QGIS Multi Ring Buffer: gisforthought.com/qgis-multi-ring-buffer OR the Multi-Distance Buffer plugins.qgis.org/plugins/MultiDistanceBuffer – HeikkiVesanto Nov 5 '15 at 15:03
  • is it for visualization only or for analysis ? – radouxju Nov 5 '15 at 16:29
  • 1
    Another vote to storing your base data in PostGIS and using a view to dynamically buffer the geometries. – ndawson Nov 5 '15 at 20:52
4

Creating a View in PostGIS to have a "live buffer" is of course the best choice if you are willing to setup a RDBMS. I was curious to see if you could also get some sort of live buffer without PostGIS, so I gave it a try with spatialite and it works too. The difference is just that it seems that you can't create a View with a different geometry-type in spatialite. I found this inforation here: https://groups.google.com/forum/#!topic/spatialite-users/ZZIPwYt8-uA there was written:

"there is absolutely no way to show a View geometry in spatialite-gis, if the geometry-class isn't exactly the same of the one used for the main-table"

So you have to use a buffer table and three triggers:

CREATE TABLE lines ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL);


SELECT AddGeometryColumn('lines', 'geometry', 3857, 'LINESTRING', 'XY');


CREATE TABLE lines_buffer (id INTEGER PRIMARY KEY AUTOINCREMENT, gid INTEGER, name TEXT NOT NULL);

SELECT AddGeometryColumn('lines_buffer', 'geo', 3857, 'POLYGON', 'XY');

CREATE TRIGGER "lines_buffer_insert" AFTER
INSERT ON "lines"
FOR EACH ROW BEGIN
INSERT INTO lines_buffer (gid, name, geo)
SELECT new.id,
       new.name,
       ST_Buffer(new.geometry, 1000) ; 
END

CREATE TRIGGER "lines_buffer_update" AFTER
UPDATE ON "lines"
FOR EACH ROW BEGIN
UPDATE lines_buffer
SET geo=(ST_Buffer(NEW.geometry, 1000))
WHERE gid=NEW.id ;
  UPDATE lines_buffer
  SET name=NEW.name WHERE gid=NEW.id ; 
END

  CREATE TRIGGER "lines_buffer_delete" AFTER
  DELETE ON "lines"
  FOR EACH ROW BEGIN
  DELETE
  FROM lines_buffer WHERE gid=OLD.id ;
 END

I havent tested it excessively, but i created, updated and deleted several lines and the buffer was always updated (after refreshing/panning QGIS of course). So this could be an alternative where you have just one spatialite file to store the data and don't have to setup a PostGIS database... enter image description here

2
  • A trigger will also work well for PostGIS, probably better than a view. – alphabetasoup Nov 6 '15 at 1:07
  • Yes, the performance while panning/zooming in qgis will probably be better. – Thomas B Nov 6 '15 at 6:59
4

Posting an answer from another question (How to add a buffer in QGIS that updates itself) as an alternative to the postgis/rdbms solutions already shared.

For a foolproof, simple visualisation, try the geometry generator.

  1. Go to Layer Styling panel -> select style -> Symbol layer type: Geometry generator
  2. select Geometry type: Polygon/Multipolygon
  3. enter the following expression buffer($geometry,AMOUNT)
  4. AMOUNT can be a fixed number in the units of your CRS, or a field in your data - in which case it would be buffer($geometry,"buffer_field")
  5. buffers will appear as soon as you add or edit a point in qgis, even if you haven't saved edits

To generate polygons that you can calculate areas of and so on, try virtual layers.

  1. Go to Layer -> Add Layer -> Add/Edit Virtual Layer
  2. click Import and select your layer
  3. enter the following query: SELECT desired_field, desired_field2, 'desired field', st_buffer(geometry,AMOUNT) AS geometry from 'Name of layer'; - desired fields are optional. Any names of layers or fields with spaces, or most non-alphanumeric characters, must be wrapped in single quotes. Click Test to check that there are no errors before clicking OK.
  4. buffers are saved as a separate layer, which will update only when you press Refresh, and any edits are saved (if editing your source layer outside qgis)
2
  • Maybe providing a link to the mentioned question would be a better approach instead of copy&pasting already given answers. At least to prevent redundancy and allow other users to compare those questions. – Nightwatch Aug 30 '17 at 7:20
  • Okay, I added a link to the question (which was marked as a duplicate of this), but I only pasted it here on the request of someone else. – she_weeds Aug 30 '17 at 7:47
3

Just do

from qgis.analysis import QgsGeometryAnalyzer

# Select your layer
layer_ref = iface.activeLayer()
# Get layer tree root
root = QgsProject.instance().layerTreeRoot()

# Prepare function that will act as slot
def save_after_edit(layer, qgsgeometrymap):
    output_filename = "output.shp"
    # Make buffer and save it in a shp
    QgsGeometryAnalyzer().buffer(layer_ref, output_filename, 500, False, False, -1)
    # If layer with a name 'buffer', remove it (only one layer 'buffer' allowed)
    if 'buffer' in [i.name() for i in QgsMapLayerRegistry.instance().mapLayers().values()]:
        QgsMapLayerRegistry.instance().removeMapLayer([i.id() for i in QgsMapLayerRegistry.instance().mapLayers().values() if i.name() == "buffer"][0])
    # Add the created buffer and add it just below the layer at the top
    if 'buffer' not in [i.name() for i in QgsMapLayerRegistry.instance().mapLayers().values()]:
        mylayer = QgsVectorLayer(output_filename, "buffer", "ogr")
        QgsMapLayerRegistry.instance().addMapLayer(mylayer, False)
        root.insertLayer(1, mylayer) # Position in the layer tree

# Use signal emitted from layer when you commit changes
# Other signals available at http://www.qgis.org/api/classQgsVectorLayer.html
layer_ref.committedGeometriesChanges.connect(save_after_edit)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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