diff --git a/src/vector/v.multi2singlepart/Makefile b/src/vector/v.multi2singlepart/Makefile new file mode 100644 index 0000000000..ccae942c11 --- /dev/null +++ b/src/vector/v.multi2singlepart/Makefile @@ -0,0 +1,7 @@ +MODULE_TOPDIR = ../.. + +PGM = v.multi2singlepart + +include $(MODULE_TOPDIR)/include/Make/Script.make + +default: script diff --git a/src/vector/v.multi2singlepart/example.png b/src/vector/v.multi2singlepart/example.png new file mode 100644 index 0000000000..484835cb56 Binary files /dev/null and b/src/vector/v.multi2singlepart/example.png differ diff --git a/src/vector/v.multi2singlepart/example.svg b/src/vector/v.multi2singlepart/example.svg new file mode 100644 index 0000000000..626619f699 --- /dev/null +++ b/src/vector/v.multi2singlepart/example.svg @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + 1 + 2 + 3 + + + + 1 + 1 + 1 + 1 multipart feature + 3 singlepart features + + + + + diff --git a/src/vector/v.multi2singlepart/v.multi2singlepart.html b/src/vector/v.multi2singlepart/v.multi2singlepart.html new file mode 100644 index 0000000000..5a5012faac --- /dev/null +++ b/src/vector/v.multi2singlepart/v.multi2singlepart.html @@ -0,0 +1,44 @@ +

DESCRIPTION

+ +v.multi2singlepart creates a vector layer containing +singlepart polygon features generated by separating the multipart +features of the input vector layer. The attributes of the input layer +will be maintained in the output vector layer. Singlepart features will +not be affected. + +

+

+From multipart to singlepart features
From multipart to singlepart polygons.
+ + +

NOTES

+ +To go from singlepart to multipart features, use the +v.dissolve function (see example below). + + +

EXAMPLES

+ +The example uses the layer boundary_municp from the North +Carolina dataset. You can download the sample data set from the GRASS GIS website + +
+
+v.dissolve input=boundary_municp column=MB_NAME output=bnd_municp_dis
+v.multi2singlepart input=bnd_municp_dis output=bnd_municp_split
+
+
+ +

SEE ALSO

+ + v.dissolve + + + + +

AUTHOR

+ +Paulo van Breugel, paulo at ecodiv.earth diff --git a/src/vector/v.multi2singlepart/v.multi2singlepart.py b/src/vector/v.multi2singlepart/v.multi2singlepart.py new file mode 100755 index 0000000000..e8f1f33a9e --- /dev/null +++ b/src/vector/v.multi2singlepart/v.multi2singlepart.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +# +############################################################################ +# +# MODULE: v.multi2singlepart +# AUTHOR(S): Paulo van Breugel +# PURPOSE: Split multipart polygon features into singlepart features +# +# COPYRIGHT: (C) 2024 Paulo van Breugel and the GRASS Development Team +# http://ecodiv.earth +# +# This program is free software under the GNU General Public +# License (>=v2). Read the file COPYING that comes with GRASS +# for details. +# +############################################################################# +# +# REQUIREMENTS: +# - +# %module +# % description: Split multi-part polygons into single-part polygons. +# % keyword: vector +# % keyword: geometry +# %end + +# %option G_OPT_V_INPUT +# % description: Input vector layer +# % required: yes +# %end + +# %option G_OPT_V_OUTPUT +# % description: Output vector layer +# % required: yes +# %end + +# ---------------------------------------------------------------------------- +# Standard +# ---------------------------------------------------------------------------- + +# import libraries +import sys +import atexit +import sys +import uuid +import grass.script as gs + + +CLEAN_LAY = [] + + +def create_temporary_name(prefix): + tmpf = f"{prefix}{str(uuid.uuid4().hex)}" + CLEAN_LAY.append(tmpf) + return tmpf + + +def cleanup(): + """Remove temporary maps specified in the global list""" + maps = reversed(CLEAN_LAY) + mapset = gs.gisenv()["MAPSET"] + for map_name in maps: + for element in ("raster", "vector"): + found = gs.find_file( + name=map_name, + element=element, + mapset=mapset, + ) + if found["file"]: + gs.run_command( + "g.remove", + flags="f", + type=element, + name=map_name, + quiet=True, + ) + + +def main(options, flags): + + # Copy layer and remove all but cat column in new layer + tmplayer = create_temporary_name("tmp") + gs.run_command("g.copy", vector=[options["input"], tmplayer], overwrite=True) + cols = gs.read_command("db.columns", table=tmplayer).split("\n") + cols = [x for x in cols[1:] if x != ""] + gs.run_command("v.db.dropcolumn", map=tmplayer, columns=cols) + + # Check topology + top = gs.parse_command("v.info", flags="t", map=tmplayer) + if int(top["areas"]) == 0: + gs.fatal(_("The layer does not contain areas. Exiting...")) + + # Overlay the original and copied layer + gs.run_command( + "v.overlay", + ainput=options["input"], + binput=tmplayer, + operator="and", + atype="area", + btype="area", + output=options["output"], + ) + + # Drop old cat columns + gs.run_command("v.db.dropcolumn", map=options["output"], columns=["a_cat", "b_cat"]) + + # Change the column names to the original names + for colname in cols: + oldcol = f"a_{colname}" + gs.run_command( + "v.db.renamecolumn", map=options["output"], column=[oldcol, colname] + ) + + +if __name__ == "__main__": + atexit.register(cleanup) + sys.exit(main(*gs.parser()))