diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..cfee739
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,16 @@
+[flake8]
+
+# E402 module level import not at top of file
+# E501 line too long (83 > 79 characters)
+# F821 undefined name '_'
+
+exclude = .git
+
+max-line-length = 87
+
+per-file-ignores =
+ ./m.neural_network.preparedata/m.neural_network.preparedata.py: F821
+ ./m.neural_network.preparedata/m.neural_network.preparedata.py: E501
+ ./m.neural_network.preparedata.worker_nullcells/m.neural_network.preparedata.worker_nullcells.py: F821
+ ./m.neural_network.preparedata.worker_nullcells/m.neural_network.preparedata.worker_nullcells.py: E501
+ ./m.neural_network.preparedata.worker_export/m.neural_network.preparedata.worker_export.py: E501
diff --git a/.github/workflows/grass-manual.yml b/.github/workflows/grass-manual.yml
new file mode 100644
index 0000000..94b4794
--- /dev/null
+++ b/.github/workflows/grass-manual.yml
@@ -0,0 +1,7 @@
+on:
+ push:
+ branches: [ main ]
+
+jobs:
+ grass-manual:
+ uses: mundialis/github-workflows/.github/workflows/grass-manual.yml@main
diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
new file mode 100644
index 0000000..73bef46
--- /dev/null
+++ b/.github/workflows/linting.yml
@@ -0,0 +1,15 @@
+---
+name: Linting and code quality check
+
+on:
+ push:
+ branches:
+ - main
+ - develop
+ pull_request:
+
+jobs:
+ lint:
+ uses: mundialis/github-workflows/.github/workflows/linting.yml@main
+ with:
+ VALIDATE_HTML: false
\ No newline at end of file
diff --git a/.github/workflows/post-pr-reviews.yml b/.github/workflows/post-pr-reviews.yml
new file mode 100644
index 0000000..25b519b
--- /dev/null
+++ b/.github/workflows/post-pr-reviews.yml
@@ -0,0 +1,12 @@
+---
+name: Post PR code suggestions
+
+on:
+ workflow_run:
+ workflows: ["Linting and code quality check"]
+ types:
+ - completed
+
+jobs:
+ post-pr-reviews:
+ uses: mundialis/github-workflows/.github/workflows/post-pr-reviews.yml@main
\ No newline at end of file
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 0000000..ef1da64
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,48 @@
+# For documentation about this config, see
+# https://pylint.readthedocs.io/en/stable/user_guide/configuration/all-options.html (as of writing, version 2.17.4)
+
+[MAIN]
+
+jobs=0 # Default: 1
+
+load-plugins=
+ pylint.extensions.broad_try_clause
+
+recursive=yes # Default: False
+
+# reports=yes # Default: False
+
+# score=no # Default: True
+
+# To disable more rules, see output of pylint. E.g.
+# [...] C0301: Line too long (89/80) (line-too-long)
+# can be suppressed with either disable=line-too-long or disable=C
+# It is also possible to ignore a specific line by adding
+# # pylint: disable=broad-exception-caught
+# above the line causing the lint error
+disable=
+ W, ; all Warnings are allowed to fail
+ import-error, ; To suppress e.g "Unable to import 'grass.script"
+ missing-module-docstring, ; we use the GRASS GIS header
+ R, ; refactoring + design recommendations
+ consider-using-enumerate, ; to supress consider using enumerate
+ line-too-long, ; >80
+
+[BASIC]
+# for global variable setting
+good-names=keep_data,download_dir,gisdbase,tgtgisrc,tmploc,srcgisrc
+
+[BROAD_TRY_CLAUSE]
+max-try-statements=4 # Default: 1
+
+[FORMAT]
+max-line-length=80 # Default: 100
+max-module-lines=800 # Default: 1000
+ignore-long-lines=.*COPYRIGHT:.* |# . description:.*|\s*(# )?.*http.:\/\/\S+?|# %%*
+
+[MESSAGES CONTROL]
+# E0606 (possibly-used-before-assignment): to set options and flags at the bottom of the file without pre-initialization
+disable=E0606
+
+[VARIABLES]
+additional-builtins=_ # Default: ()
diff --git a/.pylintrc_allowed_to_fail b/.pylintrc_allowed_to_fail
new file mode 100644
index 0000000..e00ddf0
--- /dev/null
+++ b/.pylintrc_allowed_to_fail
@@ -0,0 +1,57 @@
+# For documentation about this config, see
+# https://pylint.readthedocs.io/en/stable/user_guide/configuration/all-options.html (as of writing, version 2.17.4)
+
+[MAIN]
+
+exit-zero=yes
+
+jobs=0 # Default: 1
+
+load-plugins=
+ pylint.extensions.broad_try_clause
+
+recursive=yes # Default: False
+
+# score=no # Default: True
+
+[BASIC]
+# for global variable setting
+good-names=keep_data,download_dir,gisdbase,tgtgisrc,tmploc,srcgisrc
+
+[BROAD_TRY_CLAUSE]
+max-try-statements=4 # Default: 1
+
+[FORMAT]
+max-line-length=80 # Default: 100
+max-module-lines=800 # Default: 1000
+ignore-long-lines=.*COPYRIGHT:.* |# . description:.*|\s*(# )?.*http.:\/\/\S+?|# %%*
+
+[MESSAGES CONTROL]
+# E0606 (possibly-used-before-assignment): to set options and flags at the bottom of the file without pre-initialization
+disable=E0606
+
+[VARIABLES]
+additional-builtins=_ # Default: ()
+
+
+; [DESIGN]
+
+; # Maximum number of arguments for function / method.
+; # Default: 5
+; max-args=9
+
+; # Maximum number of attributes for a class (see R0902).
+; # Default: 7
+; max-attributes=11
+
+; # Maximum number of branch for function / method body.
+; # Default: 12
+; max-branches=15
+
+; # Maximum number of locals for function / method body.
+; # Default: 15
+; max-locals=19
+
+; # Maximum number of return / yield for function / method body.
+; # Default: 6
+; max-returns=11
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..334e66a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+MODULE_TOPDIR = ../..
+
+PGM = m.neural_network
+
+# note: to deactivate a module, just place a file "DEPRECATED" into the subdir
+ALL_SUBDIRS := ${sort ${dir ${wildcard */.}}}
+DEPRECATED_SUBDIRS := ${sort ${dir ${wildcard */DEPRECATED}}}
+RM_SUBDIRS := bin/ docs/ etc/ scripts/
+SUBDIRS_1 := $(filter-out $(DEPRECATED_SUBDIRS), $(ALL_SUBDIRS))
+SUBDIRS := $(filter-out $(RM_SUBDIRS), $(SUBDIRS_1))
+
+include $(MODULE_TOPDIR)/include/Make/Dir.make
+
+default: parsubdirs htmldir
+
+install: installsubdirs
+ $(INSTALL_DATA) $(PGM).html $(INST_DIR)/docs/html/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b80a7f5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+# m.neural_network - Toolset for creating training data and training a neural network
+
+For now the toolset only includes add-ons for data preparation for training data creation.
+
+The m.neural_network toolset consists of following modules:
+* m.neural_network.preparedata: prepare training data as first step for the process of creating a neural network.
+ * m.neural_network.preparedata.worker_nullsells: Worker module for m.neural_network.preparedata to check null cells
+ * m.neural_network.preparedata.worker_export: Worker module for m.neural_network.preparedata to export data
+* m.neural_network.preparetraining: prepare training data for use in model training
+ * m.neural_network.preparetraining.worker: Worker module for m.neural_network.preparetraining to check and rasterize label data
\ No newline at end of file
diff --git a/m.neural_network.html b/m.neural_network.html
new file mode 100644
index 0000000..e98985b
--- /dev/null
+++ b/m.neural_network.html
@@ -0,0 +1,53 @@
+
+
+
\ No newline at end of file
diff --git a/m.neural_network.preparedata.worker_nullcells/m.neural_network.preparedata.worker_nullcells.py b/m.neural_network.preparedata.worker_nullcells/m.neural_network.preparedata.worker_nullcells.py
new file mode 100644
index 0000000..a2cb37c
--- /dev/null
+++ b/m.neural_network.preparedata.worker_nullcells/m.neural_network.preparedata.worker_nullcells.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python3
+"""############################################################################
+#
+# MODULE: m.neural_network.preparedata.worker_nullcells
+# AUTHOR(S): Guido Riembauer, Anika Weinmann
+# PURPOSE: Worker module for m.neural_network.preparedata to check null
+# cells
+# COPYRIGHT: (C) 2024 by mundialis GmbH & Co. KG and the GRASS Development
+# Team.
+#
+# This program is free software under the GNU General Public
+# License (v3). Read the file COPYING that comes with GRASS
+# for details.
+#
+#############################################################################
+"""
+
+# %Module
+# % description: Worker module for m.neural_network.preparedata to check null cells.
+# % keyword: raster
+# % keyword: statistics
+# %end
+
+# %option
+# % key: n
+# % type: string
+# % required: no
+# % multiple: no
+# % key_desc: value
+# % description: Value for the northern edge
+# % guisection: Bounds
+# %end
+
+# %option
+# % key: s
+# % type: string
+# % required: no
+# % multiple: no
+# % key_desc: value
+# % description: Value for the southern edge
+# % guisection: Bounds
+# %end
+
+# %option
+# % key: e
+# % type: string
+# % required: no
+# % multiple: no
+# % key_desc: value
+# % description: Value for the eastern edge
+# % guisection: Bounds
+# %end
+
+# %option
+# % key: w
+# % type: string
+# % required: no
+# % multiple: no
+# % key_desc: value
+# % description: Value for the western edge
+# % guisection: Bounds
+# %end
+
+# %option
+# % key: res
+# % type: string
+# % required: no
+# % multiple: no
+# % key_desc: value
+# % description: 2D grid resolution (north-south and east-west)
+# % guisection: Resolution
+# %end
+
+# %option G_OPT_R_INPUT
+# % key: map
+# % label: The name of input raster map
+# % guisection: Input
+# %end
+
+# %option
+# % key: tile_name
+# % type: string
+# % required: yes
+# % multiple: no
+# % key_desc: name
+# % label: Unique Name of the tile
+# %end
+
+# %option
+# % key: new_mapset
+# % type: string
+# % required: yes
+# % multiple: no
+# % label: Name for new mapset
+# %end
+
+import os
+import shutil
+import sys
+
+import grass.script as grass
+from grass_gis_helpers.mapset import switch_to_new_mapset
+
+NEWGISRC = None
+GISRC = None
+ID = grass.tempname(8)
+NEW_MAPSET = None
+
+
+def cleanup() -> None:
+ """Clean up function switching mapsets and deleting the new one."""
+ grass.utils.try_remove(NEWGISRC)
+ os.environ["GISRC"] = GISRC
+ # delete the new mapset (doppelt haelt besser)
+ gisenv = grass.gisenv()
+ gisdbase = gisenv["GISDBASE"]
+ location = gisenv["LOCATION_NAME"]
+ mapset_dir = os.path.join(gisdbase, location, NEW_MAPSET)
+ if os.path.isdir(mapset_dir):
+ shutil.rmtree(mapset_dir)
+
+
+def main() -> None:
+ """Check null cells."""
+ global NEW_MAPSET, NEWGISRC, GISRC
+
+ NEW_MAPSET = options["new_mapset"]
+ tile_name = options["tile_name"]
+ north = options["n"]
+ south = options["s"]
+ west = options["w"]
+ east = options["e"]
+ res = options["res"]
+ map = options["map"]
+
+ # switch to the new mapset
+ GISRC, NEWGISRC, old_mapset = switch_to_new_mapset(NEW_MAPSET)
+
+ # map full name
+ if "@" not in map:
+ map += f"@{old_mapset}"
+
+ # set region
+ grass.message(_(f"Set region for tile {tile_name} ..."))
+ grass.run_command(
+ "g.region",
+ n=north,
+ s=south,
+ e=east,
+ w=west,
+ res=res,
+ quiet=True,
+ )
+
+ # get number of null cells
+ stats = grass.parse_command(
+ "r.univar",
+ map=map,
+ flags="g",
+ )
+ sys.stdout.write(
+ f"For tile {tile_name} the number of null cells is: {stats['null_cells']}\n",
+ )
+
+
+if __name__ == "__main__":
+ options, flags = grass.parser()
+ main()
diff --git a/m.neural_network.preparedata/Makefile b/m.neural_network.preparedata/Makefile
new file mode 100644
index 0000000..da01e06
--- /dev/null
+++ b/m.neural_network.preparedata/Makefile
@@ -0,0 +1,14 @@
+MODULE_TOPDIR = ../..
+
+PGM = m.neural_network.preparedata
+
+include $(MODULE_TOPDIR)/include/Make/Script.make
+
+QML_ETC = $(patsubst %,$(ETC)/$(PGM)/%,$(wildcard qml/*.qml))
+mkdir:
+ $(MKDIR) $(ETC)/$(PGM)/qml
+
+default: script mkdir $(QML_ETC)
+
+$(ETC)/$(PGM)/%: % | $(ETC)/$(PGM)
+ $(INSTALL_DATA) $< $@
diff --git a/m.neural_network.preparedata/m.neural_network.preparedata.html b/m.neural_network.preparedata/m.neural_network.preparedata.html
new file mode 100644
index 0000000..8ef3fea
--- /dev/null
+++ b/m.neural_network.preparedata/m.neural_network.preparedata.html
@@ -0,0 +1,27 @@
+
DESCRIPTION
+
+m.neural_network.preparedata prepares tiles for the labeling process as part of the
+training data preparation of a neural network using DOPs and nDSM as input data. Additionally,
+a tile index containing information about the labeled status is created.
+
+
EXAMPLES
+
+
Prepare the labeling of training data for a neural network with a tile_size of 512