changeset 5601:1222b777f51f

Made golint finally happy.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Sat, 06 Aug 2022 02:09:57 +0200
parents 9967a78e43f4
children 781628cda531
files pkg/config/config.go pkg/controllers/surveys.go pkg/imports/fm.go pkg/imports/modelconvert.go pkg/imports/queue.go pkg/imports/report.go pkg/imports/statsupdate.go pkg/imports/wfsjob.go pkg/log/log.go pkg/mesh/classbreaks.go pkg/mesh/loader.go pkg/mesh/polygon.go pkg/mesh/raster.go pkg/mesh/tin.go pkg/mesh/triangulation.go pkg/mesh/vertex.go pkg/middleware/nosniff.go pkg/misc/http.go pkg/misc/mail.go pkg/models/common.go pkg/models/cross.go pkg/models/diff.go pkg/models/extservices.go pkg/models/import.go pkg/models/importbase.go pkg/models/imports.go pkg/models/intservices.go pkg/models/isrs.go pkg/models/reproject.go pkg/models/search.go pkg/models/sr.go pkg/models/surveys.go pkg/wkb/data.go pkg/xlsx/templater.go
diffstat 34 files changed, 424 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/config/config.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/config/config.go	Sat Aug 06 02:09:57 2022 +0200
@@ -130,7 +130,7 @@
 func LogFile() string { return viper.GetString("log-file") }
 
 // LogLevel is the log level of the application.
-func LogLevel() log.LogLevel { return log.ParseLogLevel(viper.GetString("log-level")) }
+func LogLevel() log.Level { return log.ParseLogLevel(viper.GetString("log-level")) }
 
 var (
 	proxyKeyOnce       sync.Once
--- a/pkg/controllers/surveys.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/controllers/surveys.go	Sat Aug 06 02:09:57 2022 +0200
@@ -27,7 +27,7 @@
 )
 
 const (
-	listSurveysByIdSQL = `
+	listSurveysByIDSQL = `
 SELECT DISTINCT
   s.bottleneck_id,
   s.date_info::text,
@@ -47,11 +47,11 @@
 
 func listSurveys(req *http.Request) (jr mw.JSONResult, err error) {
 
-	bottleneckId := mux.Vars(req)["id"]
+	bottleneckID := mux.Vars(req)["id"]
 
 	var rows *sql.Rows
 
-	rows, err = mw.JSONConn(req).QueryContext(req.Context(), listSurveysByIdSQL, bottleneckId)
+	rows, err = mw.JSONConn(req).QueryContext(req.Context(), listSurveysByIDSQL, bottleneckID)
 	if err != nil {
 		return
 	}
--- a/pkg/imports/fm.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/imports/fm.go	Sat Aug 06 02:09:57 2022 +0200
@@ -24,7 +24,7 @@
 )
 
 type (
-	// Properties common to all types of fairway marks
+	// FairwayMarksProperties are common to all types of fairway marks
 	FairwayMarksProperties struct {
 		Datsta *string `json:"hydro_datsta"`
 		Datend *string `json:"hydro_datend"`
@@ -41,6 +41,7 @@
 		Sorind *string `json:"hydro_sorind"`
 	}
 
+	// BcnlatProperties are embedded in other properties.
 	BcnlatProperties struct {
 		FairwayMarksProperties
 		Colour *string `json:"hydro_colour"`
@@ -60,6 +61,7 @@
 		Dirimp *string `json:"ienc_dirimp" structs:"-"`
 	}
 
+	// BoylatProperties are embedded in other properties.
 	BoylatProperties struct {
 		FairwayMarksProperties
 		Colour *string `json:"hydro_colour"`
@@ -109,6 +111,7 @@
 		Catspm *string `json:"hydro_catspm"`
 	}
 
+	// DaymarProperties are embedded in other properties.
 	DaymarProperties struct {
 		FairwayMarksProperties
 		Colour *string `json:"hydro_colour"`
@@ -192,19 +195,32 @@
 }
 
 const (
+	// BCNLATHYDROJobKind is the unique name for the respective import.
 	BCNLATHYDROJobKind JobKind = "fm_bcnlat_hydro"
-	BCNLATIENCJobKind  JobKind = "fm_bcnlat_ienc"
+	// BCNLATIENCJobKind is the unique name for the respective import.
+	BCNLATIENCJobKind JobKind = "fm_bcnlat_ienc"
+	// BOYLATHYDROJobKind is the unique name for the respective import.
 	BOYLATHYDROJobKind JobKind = "fm_boylat_hydro"
-	BOYLATIENCJobKind  JobKind = "fm_boylat_ienc"
-	BOYCARJobKind      JobKind = "fm_boycar"
-	BOYSAWJobKind      JobKind = "fm_boysaw"
-	BOYSPPJobKind      JobKind = "fm_boyspp"
+	// BOYLATIENCJobKind is the unique name for the respective import.
+	BOYLATIENCJobKind JobKind = "fm_boylat_ienc"
+	// BOYCARJobKind is the unique name for the respective import.
+	BOYCARJobKind JobKind = "fm_boycar"
+	// BOYSAWJobKind is the unique name for the respective import.
+	BOYSAWJobKind JobKind = "fm_boysaw"
+	// BOYSPPJobKind is the unique name for the respective import.
+	BOYSPPJobKind JobKind = "fm_boyspp"
+	// DAYMARHYDROJobKind is the unique name for the respective import.
 	DAYMARHYDROJobKind JobKind = "fm_daymar_hydro"
-	DAYMARIENCJobKind  JobKind = "fm_daymar_ienc"
-	LIGHTSJobKind      JobKind = "fm_lights"
-	NOTMRKJobKind      JobKind = "fm_notmrk"
-	RTPBCNJobKind      JobKind = "fm_rtpbcn"
-	TOPMARJobKind      JobKind = "fm_topmar"
+	// DAYMARIENCJobKind is the unique name for the respective import.
+	DAYMARIENCJobKind JobKind = "fm_daymar_ienc"
+	// LIGHTSJobKind is the unique name for the respective import.
+	LIGHTSJobKind JobKind = "fm_lights"
+	// NOTMRKJobKind is the unique name for the respective import.
+	NOTMRKJobKind JobKind = "fm_notmrk"
+	// RTPBCNJobKind is the unique name for the respective import.
+	RTPBCNJobKind JobKind = "fm_rtpbcn"
+	// TOPMARJobKind is the unique name for the respective import.
+	TOPMARJobKind JobKind = "fm_topmar"
 )
 
 func init() {
--- a/pkg/imports/modelconvert.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/imports/modelconvert.go	Sat Aug 06 02:09:57 2022 +0200
@@ -182,7 +182,7 @@
 	DSRJobKind: func(input interface{}) interface{} {
 		dsr := input.(*models.SoundingResultDelete)
 		return &DeleteSoundingResult{
-			BottleneckID: dsr.BottleneckId,
+			BottleneckID: dsr.BottleneckID,
 			Date:         dsr.Date,
 		}
 	},
--- a/pkg/imports/queue.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/imports/queue.go	Sat Aug 06 02:09:57 2022 +0200
@@ -67,6 +67,7 @@
 		CleanUp() error
 	}
 
+	// FeedbackJob is a job to create feedback.
 	FeedbackJob interface {
 		Job
 		CreateFeedback(int64) Feedback
@@ -98,6 +99,7 @@
 		AutoAccept() bool
 	}
 
+	// JobRemover is a extented JobCreator to remove a job.
 	JobRemover interface {
 		JobCreator
 		RemoveJob() bool
@@ -120,6 +122,7 @@
 )
 
 const (
+	// ReviewJobSuffix is the prefix of review jobs.
 	ReviewJobSuffix  = "#review"
 	reviewJobRetries = 200
 	reviewJobWait    = 10 * time.Minute
@@ -130,6 +133,7 @@
 	minWaitRetry = 5 * time.Second
 )
 
+// ErrRetrying are used to signal a retry.
 var ErrRetrying = errors.New("retrying")
 
 type importQueue struct {
@@ -282,8 +286,8 @@
 
 func (*reviewedJob) CleanUp() error { return nil }
 
-func (r *reviewedJob) CreateFeedback(int64) Feedback {
-	return logFeedback(r.ID)
+func (rj *reviewedJob) CreateFeedback(int64) Feedback {
+	return logFeedback(rj.ID)
 }
 
 func (rj *reviewedJob) Do(
@@ -390,7 +394,6 @@
 	return iqueue.hasImportKindName(kind)
 }
 
-//
 func (q *importQueue) hasImportKindName(kind string) bool {
 	q.creatorsMu.Lock()
 	defer q.creatorsMu.Unlock()
@@ -722,6 +725,7 @@
 	return nil
 }
 
+// DecideImport decides if a given import is accepted or not.
 func DecideImport(
 	ctx context.Context,
 	id int64,
@@ -739,6 +743,8 @@
 	}
 }
 
+// All reports all configured job creators and there kind
+// to the given function.
 func All(fn func(JobKind, JobCreator)) { iqueue.All(fn) }
 
 type logFeedback int64
--- a/pkg/imports/report.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/imports/report.go	Sat Aug 06 02:09:57 2022 +0200
@@ -36,11 +36,14 @@
 	"github.com/xuri/excelize/v2"
 )
 
+// Report is a job to generate a report and send emails to the
+// receivers.
 type Report struct {
 	models.QueueConfigurationType
 	Name models.SafePath `json:"name"`
 }
 
+// ReportJobKind is the unique name of this import job type.
 const ReportJobKind JobKind = "report"
 
 type reportJobCreator struct{}
@@ -87,10 +90,13 @@
 // RequiresRoles enforces to be a sys_admin to run this .
 func (*Report) RequiresRoles() auth.Roles { return auth.Roles{"sys_admin"} }
 
+// Description gives a short info about relevant facts of this import.
 func (r *Report) Description([]string) (string, error) { return string(r.Name), nil }
 
+// CleanUp is an empty implementation.
 func (*Report) CleanUp() error { return nil }
 
+// MarshalAttributes implements a DB marshaling of this job.
 func (r *Report) MarshalAttributes(attrs common.Attributes) error {
 	if err := r.QueueConfigurationType.MarshalAttributes(attrs); err != nil {
 		return err
@@ -99,6 +105,7 @@
 	return nil
 }
 
+// UnmarshalAttributes implements a DB unmarshaling of this job.
 func (r *Report) UnmarshalAttributes(attrs common.Attributes) error {
 	if err := r.QueueConfigurationType.UnmarshalAttributes(attrs); err != nil {
 		return err
@@ -154,6 +161,7 @@
 	return template, action, nil
 }
 
+// Do executes the actual report generation.
 func (r *Report) Do(
 	ctx context.Context,
 	importID int64,
--- a/pkg/imports/statsupdate.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/imports/statsupdate.go	Sat Aug 06 02:09:57 2022 +0200
@@ -25,11 +25,13 @@
 	"gemma.intevation.de/gemma/pkg/models"
 )
 
+// StatsUpdate is job for status updates.
 type StatsUpdate struct {
 	models.QueueConfigurationType
 	Name string `json:"name"`
 }
 
+// StatsUpdateJobKind is the unique name of this import job type.
 const StatsUpdateJobKind JobKind = "statsupdate"
 
 type statsUpdateJobCreator struct{}
@@ -51,10 +53,13 @@
 // RequiresRoles enforces to be a sys_admin to run this .
 func (*StatsUpdate) RequiresRoles() auth.Roles { return auth.Roles{"sys_admin"} }
 
+// Description gives a short info about relevant facts of this import.
 func (su *StatsUpdate) Description([]string) (string, error) { return su.Name, nil }
 
+// CleanUp is an empty implementation.
 func (*StatsUpdate) CleanUp() error { return nil }
 
+// MarshalAttributes implements DB marshaling of this job.
 func (su *StatsUpdate) MarshalAttributes(attrs common.Attributes) error {
 	if err := su.QueueConfigurationType.MarshalAttributes(attrs); err != nil {
 		return err
@@ -63,6 +68,7 @@
 	return nil
 }
 
+// UnmarshalAttributes implements DB unmarshaling this job.
 func (su *StatsUpdate) UnmarshalAttributes(attrs common.Attributes) error {
 	if err := su.QueueConfigurationType.UnmarshalAttributes(attrs); err != nil {
 		return err
@@ -77,6 +83,7 @@
 
 const loadUpdateStatsScriptSQL = `SELECT script FROM sys_admin.stats_updates WHERE name = $1`
 
+// Do executes the actual report generation.
 func (su *StatsUpdate) Do(
 	ctx context.Context,
 	importID int64,
--- a/pkg/imports/wfsjob.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/imports/wfsjob.go	Sat Aug 06 02:09:57 2022 +0200
@@ -28,12 +28,17 @@
 )
 
 var (
-	ErrFeatureIgnored     = errors.New("feature ignored")
-	ErrFeatureDuplicated  = errors.New("feature duplicated")
+	// ErrFeatureIgnored indicates if a feature is ignored.
+	ErrFeatureIgnored = errors.New("feature ignored")
+	// ErrFeatureDuplicated indicates if a feature is a duplicate.
+	ErrFeatureDuplicated = errors.New("feature duplicated")
+	// ErrFeaturesUnmodified indicates if a feature is unmodified.
 	ErrFeaturesUnmodified = errors.New("features unmodified")
 )
 
 type (
+	// WFSFeatureConsumer is an interface to model a transactional
+	// handling to create new features.
 	WFSFeatureConsumer interface {
 		Commit() error
 		Rollback() error
@@ -43,6 +48,7 @@
 		Consume(geom, properties interface{}, epsg int) error
 	}
 
+	// WFSFeatureJobCreator is a factory to create feature consumers.
 	WFSFeatureJobCreator struct {
 		description string
 		depends     [2][]string
@@ -52,6 +58,7 @@
 		stageDone func(context.Context, *sql.Tx, int64, Feedback) error
 	}
 
+	// WFSFeatureJob is job to do an WFS import.
 	WFSFeatureJob struct {
 		models.WFSImport
 		creator *WFSFeatureJobCreator
@@ -74,18 +81,23 @@
 	}
 )
 
+// Description gives a short description of this creator.
 func (wfjc *WFSFeatureJobCreator) Description() string {
 	return wfjc.description
 }
 
+// Depends lists the dependencies of this creator.
 func (wfjc *WFSFeatureJobCreator) Depends() [2][]string {
 	return wfjc.depends
 }
 
+// AutoAccept auto accepts this job if there
+// is no stageDone implementation.
 func (wfjc *WFSFeatureJobCreator) AutoAccept() bool {
 	return wfjc.stageDone == nil
 }
 
+// StageDone forwards the StageDone call.
 func (wfjc *WFSFeatureJobCreator) StageDone(
 	ctx context.Context,
 	tx *sql.Tx,
@@ -98,6 +110,7 @@
 	return wfjc.stageDone(ctx, tx, id, feedback)
 }
 
+// Create creates the WFS job.
 func (wfjc *WFSFeatureJobCreator) Create() Job {
 	return &WFSFeatureJob{creator: wfjc}
 }
@@ -112,6 +125,7 @@
 	return nil
 }
 
+// Do implements the actual WFS import.
 func (wfj *WFSFeatureJob) Do(
 	ctx context.Context,
 	importID int64,
@@ -279,19 +293,19 @@
 	return nil, err
 }
 
-type (
-	SQLGeometryConsumer struct {
-		ctx        context.Context
-		tx         *sql.Tx
-		feedback   Feedback
-		consume    func(*SQLGeometryConsumer, interface{}, interface{}, int) error
-		newFeature func() (string, interface{})
-		preCommit  func(*SQLGeometryConsumer) error
-		savepoint  func(func() error) error
-		stmts      []*sql.Stmt
-	}
-)
+// SQLGeometryConsumer stores a WFS feature into the DB.
+type SQLGeometryConsumer struct {
+	ctx        context.Context
+	tx         *sql.Tx
+	feedback   Feedback
+	consume    func(*SQLGeometryConsumer, interface{}, interface{}, int) error
+	newFeature func() (string, interface{})
+	preCommit  func(*SQLGeometryConsumer) error
+	savepoint  func(func() error) error
+	stmts      []*sql.Stmt
+}
 
+// Rollback rolls back the database state of this consumer.
 func (sgc *SQLGeometryConsumer) Rollback() error {
 	if tx := sgc.tx; tx != nil {
 		sgc.releaseStmts()
@@ -302,6 +316,7 @@
 	return nil
 }
 
+// Commit commits the database changes of this consumer to the database.
 func (sgc *SQLGeometryConsumer) Commit() error {
 	var err error
 	if tx := sgc.tx; tx != nil {
@@ -321,10 +336,12 @@
 	return err
 }
 
+// NewFeature forwards the feature creation.
 func (sgc *SQLGeometryConsumer) NewFeature() (string, interface{}) {
 	return sgc.newFeature()
 }
 
+// Consume forwards the consumption of the given feature.
 func (sgc *SQLGeometryConsumer) Consume(
 	geom, properties interface{},
 	epsg int,
@@ -332,6 +349,7 @@
 	return sgc.consume(sgc, geom, properties, epsg)
 }
 
+// ConsumePolygon forwards the consumption of a polygon.
 func (sgc *SQLGeometryConsumer) ConsumePolygon(
 	polygon polygonSlice,
 	properties interface{},
--- a/pkg/log/log.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/log/log.go	Sat Aug 06 02:09:57 2022 +0200
@@ -22,14 +22,21 @@
 	"sync/atomic"
 )
 
-type LogLevel uint32
+// Level is an enumeration symbolizing a log level.
+type Level uint32
 
 const (
-	TraceLogLevel = LogLevel(iota)
+	// TraceLogLevel is the TRACE log level.
+	TraceLogLevel = Level(iota)
+	// DebugLogLevel is the DEBUG log level.
 	DebugLogLevel
+	// InfoLogLevel is the INFO log level.
 	InfoLogLevel
+	// WarnLogLevel is the WARN log level.
 	WarnLogLevel
+	// ErrorLogLevel is the ERROR log level.
 	ErrorLogLevel
+	// FatalLogLevel is the FATAL log level.
 	FatalLogLevel
 )
 
@@ -45,6 +52,10 @@
 
 const callDepth = 2
 
+// SetupLog redirects the log output to a given file.
+// The file is opened in append mode with the given
+// permisssions. A previously opened log file will
+// be closed.
 func SetupLog(filename string, perm os.FileMode) error {
 	f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, perm)
 	if err != nil {
@@ -60,6 +71,8 @@
 	return nil
 }
 
+// ShutdownLog closes an open log file (if there is any in use)
+// and redirects the output to stderr.
 func ShutdownLog() {
 	logFileMu.Lock()
 	defer logFileMu.Unlock()
@@ -70,7 +83,11 @@
 	lg.SetOutput(os.Stderr)
 }
 
-func ParseLogLevel(s string) LogLevel {
+// ParseLogLevel converts a strings representation
+// of a log level to the reprective log level.
+// If the log level is unknown InfoLogLevel is
+// returned.
+func ParseLogLevel(s string) Level {
 	switch strings.ToLower(s) {
 	case "trace":
 		return TraceLogLevel
@@ -89,7 +106,8 @@
 	}
 }
 
-func (level LogLevel) String() string {
+// String implements the fmt.Stringer interface.
+func (level Level) String() string {
 	switch level {
 	case TraceLogLevel:
 		return "trace"
@@ -108,14 +126,17 @@
 	}
 }
 
-func GetLogLevel() LogLevel {
-	return LogLevel(atomic.LoadUint32(&logLevel))
+// GetLogLevel returns the currently active log level.
+func GetLogLevel() Level {
+	return Level(atomic.LoadUint32(&logLevel))
 }
 
-func SetLogLevel(level LogLevel) {
+// SetLogLevel sets the log level to be active.
+func SetLogLevel(level Level) {
 	atomic.StoreUint32(&logLevel, uint32(level))
 }
 
+// Tracef formats a log message as a TRACE output.
 func Tracef(f string, args ...interface{}) {
 	if TraceLogLevel >= GetLogLevel() {
 		s := fmt.Sprintf(f, args...)
@@ -123,12 +144,14 @@
 	}
 }
 
+// Traceln line prints a log message as a TRACE output.
 func Traceln(s string) {
 	if TraceLogLevel >= GetLogLevel() {
 		lg.Output(callDepth, "[TRACE] "+s)
 	}
 }
 
+// Debugf formats a log message as a DEBUG output.
 func Debugf(f string, args ...interface{}) {
 	if DebugLogLevel >= GetLogLevel() {
 		s := fmt.Sprintf(f, args...)
@@ -136,12 +159,14 @@
 	}
 }
 
+// Debugln line prints a log message as a DEBUG output.
 func Debugln(s string) {
 	if DebugLogLevel >= GetLogLevel() {
 		lg.Output(callDepth, "[DEBUG] "+s)
 	}
 }
 
+// Infof formats a log message as a INFO output.
 func Infof(f string, args ...interface{}) {
 	if InfoLogLevel >= GetLogLevel() {
 		s := fmt.Sprintf(f, args...)
@@ -149,12 +174,14 @@
 	}
 }
 
+// Infoln line prints a log message as a INFO output.
 func Infoln(s string) {
 	if InfoLogLevel >= GetLogLevel() {
 		lg.Output(callDepth, "[INFO] "+s)
 	}
 }
 
+// Warnf formats a log message as a WARN output.
 func Warnf(f string, args ...interface{}) {
 	if WarnLogLevel >= GetLogLevel() {
 		s := fmt.Sprintf(f, args...)
@@ -162,12 +189,14 @@
 	}
 }
 
+// Warnln line prints a log message as a WARN output.
 func Warnln(s string) {
 	if WarnLogLevel >= GetLogLevel() {
 		lg.Output(callDepth, "[WARN] "+s)
 	}
 }
 
+// Errorf formats a log message as an ERROR output.
 func Errorf(f string, args ...interface{}) {
 	if ErrorLogLevel >= GetLogLevel() {
 		s := fmt.Sprintf(f, args...)
@@ -175,12 +204,15 @@
 	}
 }
 
+// Errorln line prints a log message as an ERROR output.
 func Errorln(s string) {
 	if ErrorLogLevel >= GetLogLevel() {
 		lg.Output(callDepth, "[ERROR] "+s)
 	}
 }
 
+// Fatalf formats a log message as a FATAL output
+// and terminates the programs with an error code (1).
 func Fatalf(f string, args ...interface{}) {
 	if FatalLogLevel >= GetLogLevel() {
 		s := fmt.Sprintf(f, args...)
@@ -189,6 +221,8 @@
 	}
 }
 
+// Fatalln line prints a log message as a FATAL output
+// and terminates the programs with an error code (1).
 func Fatalln(s string) {
 	if FatalLogLevel >= GetLogLevel() {
 		lg.Output(callDepth, "[FATAL] "+s)
@@ -196,12 +230,16 @@
 	}
 }
 
+// Panicf formats a log message as a PANIC output
+// and throws a panic.
 func Panicf(f string, args ...interface{}) {
 	s := fmt.Sprintf(f, args...)
 	lg.Output(callDepth, "[PANIC] "+s)
 	panic(s)
 }
 
+// Panicln line prints a log message as a PANIC output
+// and throws a panic.
 func Panicln(s string) {
 	lg.Output(callDepth, "[PANIC] "+s)
 	panic(s)
--- a/pkg/mesh/classbreaks.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/mesh/classbreaks.go	Sat Aug 06 02:09:57 2022 +0200
@@ -31,8 +31,11 @@
 WHERE config_key = $1`
 )
 
+// ClassBreaks represents borders between scalar classes.
 type ClassBreaks []float64
 
+// SampleDiffHeights generate a gradient from min to max with a
+// given step width.
 func SampleDiffHeights(min, max, step float64) ClassBreaks {
 	var heights ClassBreaks
 	switch {
@@ -62,6 +65,9 @@
 	return heights
 }
 
+// ParseClassBreaks deserialized class breaks from a
+// given string. key/value pairs a separated with
+// ','. Key and values are separated by ':'.
 func ParseClassBreaks(config string) (ClassBreaks, error) {
 
 	parts := strings.Split(config, ",")
@@ -84,6 +90,8 @@
 	return classes, nil
 }
 
+// LoadClassBreaks loads a class break from the database.
+// The given key identifies the dataset.
 func LoadClassBreaks(ctx context.Context, tx *sql.Tx, key string) (ClassBreaks, error) {
 
 	var config sql.NullString
@@ -106,6 +114,10 @@
 	return math.Round(v*10000) / 10000
 }
 
+// ExtrapolateClassBreaks extends the given
+// classbreaks (as a copy) from min to max.
+// The border class sizes are used to extrapolate
+// step sizes.
 func (cbs ClassBreaks) ExtrapolateClassBreaks(min, max float64) ClassBreaks {
 	if min > max {
 		min, max = max, min
@@ -153,10 +165,15 @@
 	return n
 }
 
+// Dedup removes duplicates from the class breaks.
 func (cbs ClassBreaks) Dedup() ClassBreaks {
 	return ClassBreaks(common.DedupFloat64s(cbs))
 }
 
+// Classify associates a class to all points of
+// a given multi points and returns a slice of
+// multi points containing the points that are
+// in the respective class.
 func (cbs ClassBreaks) Classify(points MultiPointZ) []MultiPointZ {
 	if len(cbs) == 0 {
 		return nil
--- a/pkg/mesh/loader.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/mesh/loader.go	Sat Aug 06 02:09:57 2022 +0200
@@ -91,6 +91,7 @@
 	return s.deserializeBBoxes(r)
 }
 
+// FromBytes restores a STRTree from a binary representation.
 func (s *STRTree) FromBytes(data []byte) error {
 	r, err := gzip.NewReader(bytes.NewReader(data))
 	if err != nil {
@@ -99,6 +100,7 @@
 	return s.deserialize(bufio.NewReader(r))
 }
 
+// Deserialize restores a TIN from a binary representation.
 func (t *Tin) Deserialize(r *bufio.Reader) error {
 
 	if err := binary.Read(r, binary.LittleEndian, &t.EPSG); err != nil {
--- a/pkg/mesh/polygon.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/mesh/polygon.go	Sat Aug 06 02:09:57 2022 +0200
@@ -28,19 +28,26 @@
 type (
 	ring []float64
 
+	// Polygon has a border and holes.
+	// The line segments are spatially indexed.
 	Polygon struct {
 		rings   []ring
 		indices []*rtree.RTree
 	}
 
+	// IntersectionType represents an enum
+	// of the type of intersection.
 	IntersectionType byte
 
 	lineSegment []float64
 )
 
 const (
+	// IntersectionInside is inside the polygon.
 	IntersectionInside IntersectionType = iota
+	// IntersectionOutSide is outside the polygon.
 	IntersectionOutSide
+	// IntersectionOverlaps overlaps the polygon.
 	IntersectionOverlaps
 )
 
@@ -67,6 +74,7 @@
 	return min, max
 }
 
+// Indexify creates a spatial index for thw polygon.
 func (p *Polygon) Indexify() {
 	indices := make([]*rtree.RTree, len(p.rings))
 
@@ -212,6 +220,8 @@
 	return true
 }
 
+// IntersectionBox2D checks the type of intersection of the
+// given box.
 func (p *Polygon) IntersectionBox2D(box Box2D) IntersectionType {
 
 	if len(p.rings) == 0 {
@@ -253,6 +263,8 @@
 	return IntersectionOutSide
 }
 
+// IntersectionWithTriangle checks the intersection type
+// for the given triangle.
 func (p *Polygon) IntersectionWithTriangle(t *Triangle) IntersectionType {
 	box := t.BBox()
 	min, max := box.Rect()
@@ -387,6 +399,7 @@
 	return raySlope >= diagSlope
 }
 
+// NumVertices returns the number of vertices of a given ring.
 func (p *Polygon) NumVertices(ring int) int {
 	if ring < 0 || ring >= len(p.rings) {
 		return 0
@@ -394,6 +407,8 @@
 	return len(p.rings[ring]) / 2
 }
 
+// Vertices passes the vertices of a given ring
+// to the given fn function.
 func (p *Polygon) Vertices(ring int, fn func(float64, float64)) {
 	if ring < 0 || ring >= len(p.rings) {
 		return
@@ -404,6 +419,7 @@
 	}
 }
 
+// AsWKB serializes the polygon as WKB.
 func (p *Polygon) AsWKB() []byte {
 
 	size := 1 + 4 + 4
@@ -428,6 +444,7 @@
 	return buf.Bytes()
 }
 
+// FromWKB deserializes a polygon from WKB.
 func (p *Polygon) FromWKB(data []byte) error {
 
 	r := bytes.NewReader(data)
@@ -497,6 +514,7 @@
 	return nil
 }
 
+// FromLineStringZ creates a polygon from a given linestring z.
 func (p *Polygon) FromLineStringZ(ls LineStringZ) {
 	r := make([]float64, 2*len(ls))
 	var pos int
--- a/pkg/mesh/raster.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/mesh/raster.go	Sat Aug 06 02:09:57 2022 +0200
@@ -26,6 +26,8 @@
 	"gemma.intevation.de/gemma/pkg/wkb"
 )
 
+// Raster represents a 2D cell raster with a given extend.
+// The cells are equally sized in X and Y direction.
 type Raster struct {
 	BBox     Box2D
 	CellSize float64
@@ -36,6 +38,8 @@
 
 const noData = -math.MaxFloat64
 
+// NewRaster creates a new Raster with the given extend
+// and cell size.
 func NewRaster(bbox Box2D, cellSize float64) *Raster {
 
 	width, height := bbox.Size()
@@ -61,6 +65,9 @@
 	}
 }
 
+// Rasterize fills the raster with the evaluation data
+// received via the given eval function. The cell values
+// 4x oversampled,
 func (r *Raster) Rasterize(eval func(float64, float64) (float64, bool)) {
 	var wg sync.WaitGroup
 
@@ -117,6 +124,9 @@
 	wg.Wait()
 }
 
+// Diff updates the cell values of the raster with difference
+// of this raster and the values from the gien eval function.
+// The data will 4x over-sampled.
 func (r *Raster) Diff(eval func(float64, float64) (float64, bool)) {
 	var wg sync.WaitGroup
 
@@ -181,6 +191,9 @@
 	wg.Wait()
 }
 
+// ZExtent returns the z-range of the raster.
+// The last return value is false if the raster
+// only consists of no data values.
 func (r *Raster) ZExtent() (float64, float64, bool) {
 	min, max := math.MaxFloat64, -math.MaxFloat64
 	for _, v := range r.Cells {
@@ -197,6 +210,8 @@
 	return min, max, min != math.MaxFloat64
 }
 
+// Trace generates contour lines of this raster given
+// the given class breaks.
 func (r *Raster) Trace(heights ClassBreaks) []wkb.MultiPolygonGeom {
 	start := time.Now()
 
--- a/pkg/mesh/tin.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/mesh/tin.go	Sat Aug 06 02:09:57 2022 +0200
@@ -46,6 +46,8 @@
 	Max Vertex
 }
 
+// Clip returns a map of ids of triangles which are not inside the
+// given polygon.
 func (t *Tin) Clip(polygon *Polygon) map[int32]struct{} {
 	var tree STRTree
 	tree.Build(t)
--- a/pkg/mesh/triangulation.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/mesh/triangulation.go	Sat Aug 06 02:09:57 2022 +0200
@@ -26,6 +26,9 @@
 	"gonum.org/v1/gonum/stat"
 )
 
+// Triangulation represents a triangulation
+// consisting of its vertices, the convex hull,
+// the triangles and a half edge representation.
 type Triangulation struct {
 	Points     []Vertex
 	ConvexHull []Vertex
@@ -40,6 +43,12 @@
 	return &Triangulation{points, t.convexHull(), t.triangles, t.halfedges}, err
 }
 
+// EstimateTooLong estimates an edge length which is too long
+// to be a real triangle in the triangulation.
+// It is assumed that the triangles in this mesh are
+// more or less equally sized. If an edge length is more
+// than the 3.5 of the standard dev of the medium length
+// it is considered as too long.
 func (t *Triangulation) EstimateTooLong() float64 {
 
 	num := len(t.Triangles) / 3
@@ -69,6 +78,9 @@
 	return 3.5 * std
 }
 
+// ConcaveHull constructs a concave hull for this mesh,
+// Triangles that are considered as too large based on
+// the EstimateTooLong estimation are removed from the border.
 func (t *Triangulation) ConcaveHull(tooLong float64) (LineStringZ, map[int32]struct{}) {
 
 	if tooLong <= 0 {
@@ -239,6 +251,8 @@
 	return polygon, removed
 }
 
+// TriangleSlices returns the indices of the triangles
+// of the mesh as slices og length three.
 func (t *Triangulation) TriangleSlices() [][]int32 {
 	sl := make([][]int32, len(t.Triangles)/3)
 	var j int
@@ -249,6 +263,7 @@
 	return sl
 }
 
+// Tin creates a TIN out of this triangulation.
 func (t *Triangulation) Tin() *Tin {
 
 	min := Vertex{math.MaxFloat64, math.MaxFloat64, math.MaxFloat64}
--- a/pkg/mesh/vertex.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/mesh/vertex.go	Sat Aug 06 02:09:57 2022 +0200
@@ -1156,7 +1156,7 @@
 	return n
 }
 
-// MinMaxVertex returns the extend of the point set.
+// MinMax returns the extend of the point set.
 func (mpz MultiPointZ) MinMax() (Vertex, Vertex) {
 	min := Vertex{math.MaxFloat64, math.MaxFloat64, math.MaxFloat64}
 	max := Vertex{-math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64}
--- a/pkg/middleware/nosniff.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/middleware/nosniff.go	Sat Aug 06 02:09:57 2022 +0200
@@ -15,6 +15,8 @@
 
 import "net/http"
 
+// NoSniff returns a wrapper middleware which adds
+// sameorigin and nosniff headers to the delivered response.
 func NoSniff(next http.Handler) http.Handler {
 	return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
 		res.Header().Set("X-Frame-Options", "sameorigin")
--- a/pkg/misc/http.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/misc/http.go	Sat Aug 06 02:09:57 2022 +0200
@@ -38,7 +38,14 @@
 	return StoreUploadedFileCheck(req, field, fname, maxSize, false)
 }
 
-func StoreUploadedFileCheck(req *http.Request, field, fname string, maxSize int64, errorOverMax bool) (string, error) {
+// StoreUploadedFileCheck does the same as StoreUploadedFile
+// with the optional check if the upload is too large or not.
+func StoreUploadedFileCheck(
+	req *http.Request,
+	field, fname string,
+	maxSize int64,
+	errorOverMax bool,
+) (string, error) {
 
 	// Check for direct upload.
 	f, _, err := req.FormFile(field)
--- a/pkg/misc/mail.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/misc/mail.go	Sat Aug 06 02:09:57 2022 +0200
@@ -21,11 +21,14 @@
 	"gemma.intevation.de/gemma/pkg/config"
 )
 
+// EmailReceiver is the name and address of an email receiver.
 type EmailReceiver struct {
 	Name    string
 	Address string
 }
 
+// EmailAttachment is a email attachment with
+// a file name and a content.
 type EmailAttachment struct {
 	Name    string
 	Content []byte
@@ -54,6 +57,7 @@
 	return d.DialAndSend(m)
 }
 
+// SendMailToAll sends emails to all receivers,
 func SendMailToAll(
 	receivers []EmailReceiver,
 	subject string,
--- a/pkg/models/common.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/common.go	Sat Aug 06 02:09:57 2022 +0200
@@ -34,7 +34,9 @@
 const WGS84 = 4326
 
 type (
+	// Date is the common on-wire date represention.
 	Date struct{ time.Time }
+	// Time is the common on-wire time represention.
 	Time struct{ time.Time }
 
 	// Country is a valid country 2 letter code.
@@ -46,10 +48,12 @@
 	SafePath string
 )
 
+// MarshalJSON implements the json.Marshaler interface.
 func (d Date) MarshalJSON() ([]byte, error) {
 	return json.Marshal(d.Format(common.DateFormat))
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (d *Date) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -62,10 +66,12 @@
 	return err
 }
 
+// MarshalJSON implements the json.Marshaler interface.
 func (t Time) MarshalJSON() ([]byte, error) {
 	return json.Marshal(t.Format(common.TimeFormat))
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (t *Time) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -127,6 +133,7 @@
 	return
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (uc *UniqueCountries) UnmarshalJSON(data []byte) error {
 	var countries []Country
 	if err := json.Unmarshal(data, &countries); err != nil {
@@ -154,10 +161,13 @@
 	return b.String()
 }
 
+// SafePathExp is used to check if a path consists only
+// of harmless runes.
 const SafePathExp = "[a-zA-Z0-9_-]+"
 
 var safePathRegExp = regexp.MustCompile("^" + SafePathExp + "$")
 
+// Valid checks is the given path is safe.
 func (sp SafePath) Valid() bool {
 	return safePathRegExp.MatchString(string(sp))
 }
--- a/pkg/models/cross.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/cross.go	Sat Aug 06 02:09:57 2022 +0200
@@ -27,52 +27,72 @@
 )
 
 type (
-	GeoJSONFeature        string
+	// GeoJSONFeature is a GeoJSON feature.
+	GeoJSONFeature string
+	// GeoJSONLineStringType is a GeoJSON line string type.
 	GeoJSONLineStringType string
 
+	// GeoJSONDate is used to represent dates in GeoJSON.
 	GeoJSONDate struct{ time.Time }
 
+	// GeoJSONLineString is used to represent a linestring
+	// as GeoJSON.
 	GeoJSONLineString struct {
 		Type GeoJSONLineStringType `json:"type"`
 	}
 
+	// GeoJSONCoordinate is a lat/lon coordinate in GeoJSON.
 	GeoJSONCoordinate struct {
 		Lat float64
 		Lon float64
 	}
 
+	// GeoJSONCoordinateZ is a lat/lon coordinate with a z value.
 	GeoJSONCoordinateZ struct {
 		Lat float64
 		Lon float64
 		Z   float64
 	}
 
+	// CrossSectionInputProperties models the input
+	// parameters for a cross section.
 	CrossSectionInputProperties struct {
 		Bottleneck string      `json:"bottleneck"`
 		Date       GeoJSONDate `json:"date"`
 	}
 
-	GeoJSONLineCoordinates  []GeoJSONCoordinate
+	// GeoJSONLineCoordinates is a list of lat/lon coordinates.
+	GeoJSONLineCoordinates []GeoJSONCoordinate
+	// GeoJSONLineCoordinatesZ is a list of lat/lon/z coordinates.
 	GeoJSONLineCoordinatesZ []GeoJSONCoordinateZ
 
+	// GeoJSONMultiLineCoordinatesZ is a list of list of lat/lon/z coords.
 	GeoJSONMultiLineCoordinatesZ []GeoJSONLineCoordinatesZ
 
+	// CrossSectionInputGeometry models the input geometry
+	// of a cross section.
 	CrossSectionInputGeometry struct {
 		Type        GeoJSONLineStringType  `json:"type"`
 		Coordinates GeoJSONLineCoordinates `json:"coordinates"`
 	}
 
+	// CrossSectionInput is combination of geometry and
+	// attributes of a cross section input.
 	CrossSectionInput struct {
 		Type       GeoJSONFeature              `json:"type"`
 		Geometry   CrossSectionInputGeometry   `json:"geometry"`
 		Properties CrossSectionInputProperties `json:"properties"`
 	}
 
+	// CrossSectionOutputGeometry is the geometry part of
+	// the cross section output.
 	CrossSectionOutputGeometry struct {
 		Type        string                       `json:"type"`
 		Coordinates GeoJSONMultiLineCoordinatesZ `json:"coordinates"`
 	}
 
+	// CrossSectionOutput is the combination of the geometry
+	// and the properties of a cross section output.
 	CrossSectionOutput struct {
 		Type       string                     `json:"type"`
 		Geometry   CrossSectionOutputGeometry `json:"geometry"`
@@ -87,6 +107,7 @@
 	errTooLessCoordinates      = errors.New("too less coordinates")
 )
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (lc *GeoJSONLineCoordinates) UnmarshalJSON(data []byte) error {
 	var coords []GeoJSONCoordinate
 	if err := json.Unmarshal(data, &coords); err != nil {
@@ -99,6 +120,7 @@
 	return nil
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (d *GeoJSONDate) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -112,6 +134,7 @@
 	return nil
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (c *GeoJSONCoordinate) UnmarshalJSON(data []byte) error {
 	var pos []float64
 	if err := json.Unmarshal(data, &pos); err != nil {
@@ -124,6 +147,7 @@
 	return nil
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (t *GeoJSONFeature) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -136,6 +160,7 @@
 	return nil
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (t *GeoJSONLineStringType) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -148,6 +173,7 @@
 	return nil
 }
 
+// AsWKB serializes a line string as WKB.
 func (lc GeoJSONLineCoordinates) AsWKB() []byte {
 
 	size := 1 + 4 + 4 + len(lc)*(2*8)
@@ -167,12 +193,14 @@
 	return buf.Bytes()
 }
 
+// MarshalJSON implements ths json.Marshaler interface.
 func (cz GeoJSONCoordinateZ) MarshalJSON() ([]byte, error) {
 	var buf bytes.Buffer
 	fmt.Fprintf(&buf, "[%.8f,%.8f,%.8f]", cz.Lat, cz.Lon, cz.Z)
 	return buf.Bytes(), nil
 }
 
+// Scan implements sql.Scanner interface.
 func (lcz *GeoJSONLineCoordinatesZ) Scan(src interface{}) error {
 	data, ok := src.([]byte)
 	if !ok {
@@ -181,6 +209,7 @@
 	return lcz.FromWKB(data)
 }
 
+// FromWKB deserializes a line string from WKB.
 func (lcz *GeoJSONLineCoordinatesZ) FromWKB(data []byte) error {
 
 	r := bytes.NewReader(data)
@@ -231,6 +260,7 @@
 	return nil
 }
 
+// Equals checks if two coordinates are equal.
 func (cz GeoJSONCoordinateZ) Equals(other GeoJSONCoordinateZ) bool {
 	const (
 		xyEps = 1e-7
@@ -243,6 +273,7 @@
 
 func deg2rad(d float64) float64 { return d * math.Pi / 180.0 }
 
+// Distance calculates the spherical distance between two coords.
 func (cz GeoJSONCoordinateZ) Distance(other GeoJSONCoordinateZ) float64 {
 
 	const EarthRadius = 6378137.0
@@ -258,6 +289,7 @@
 	return math.Sqrt(dLat*dLat+x*x) * EarthRadius
 }
 
+// FromWKB deserializes a line strinf from WKB.
 func (mls *GeoJSONMultiLineCoordinatesZ) FromWKB(data []byte) error {
 	r := bytes.NewReader(data)
 
@@ -345,6 +377,7 @@
 	return nil
 }
 
+// Scan implements the sql.Scanner interface.
 func (mls *GeoJSONMultiLineCoordinatesZ) Scan(src interface{}) error {
 	if src == nil {
 		return nil
--- a/pkg/models/diff.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/diff.go	Sat Aug 06 02:09:57 2022 +0200
@@ -13,6 +13,9 @@
 
 package models
 
+// DiffCalculationInput is used to calculate
+// the difference of the sounding results at
+// the same bottlenec at different times.
 type DiffCalculationInput struct {
 	Minuend    Date   `json:"minuend"`
 	Subtrahend Date   `json:"subtrahend"`
--- a/pkg/models/extservices.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/extservices.go	Sat Aug 06 02:09:57 2022 +0200
@@ -23,22 +23,27 @@
 	"gemma.intevation.de/gemma/pkg/log"
 )
 
+// ExtEntry is an external WFS service.
 type ExtEntry struct {
 	Name string `json:"name"`
 	URL  string `json:"url"`
 	WFS  bool   `json:"wfs"`
 }
 
+// ExtServices is a list of the external services.
 type ExtServices struct {
 	mu      sync.Mutex
 	entries []ExtEntry
 }
 
+// ExternalServices is the list of external services
+// the Gemma server manages as proxies.
 var ExternalServices = &ExtServices{}
 
 const selectExternalServices = `SELECT local_name, remote_url, is_wfs
 FROM sys_admin.external_services ORDER BY local_name`
 
+// Find looks for a specific configures external service.
 func (es *ExtServices) Find(name string) (string, bool) {
 	es.mu.Lock()
 	defer es.mu.Unlock()
@@ -84,16 +89,24 @@
 		})
 }
 
+// Invalidate invalidate the list of external services.
 func (es *ExtServices) Invalidate() {
 	es.mu.Lock()
 	es.entries = nil
 	es.mu.Unlock()
 }
 
-func ExternalAll(ExtEntry) bool       { return true }
+// ExternalAll passes all external entries as filter.
+func ExternalAll(ExtEntry) bool { return true }
+
+// ExternalWMS passes only external entries which are not WFSs.
 func ExternalWMS(entry ExtEntry) bool { return !entry.WFS }
+
+// ExternalWFS passes only external enteries which are WFS.
 func ExternalWFS(entry ExtEntry) bool { return entry.WFS }
 
+// Filter returns a list of external services which met
+// the condition of the given accept filter.
 func (es *ExtServices) Filter(accept func(ExtEntry) bool) []ExtEntry {
 	es.mu.Lock()
 	defer es.mu.Unlock()
--- a/pkg/models/import.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/import.go	Sat Aug 06 02:09:57 2022 +0200
@@ -22,8 +22,10 @@
 )
 
 type (
+	// ImportTime is used as a time model.
 	ImportTime struct{ time.Time }
 
+	// Import is the on-wire representation of an import.
 	Import struct {
 		ID       int64      `json:"id"`
 		State    string     `json:"state"`
@@ -37,14 +39,18 @@
 		Warnings bool       `json:"warnings,omitempty"`
 	}
 
+	// ImportLogEntry is the on-wire representation of
+	// a log entry.
 	ImportLogEntry struct {
 		Time    ImportTime `json:"time"`
 		Kind    string     `json:"kind"`
 		Message string     `json:"message"`
 	}
 
+	// ReviewState is the state of a review.
 	ReviewState string
 
+	// Review is the review state for a given ID.
 	Review struct {
 		ID    int64       `json:"id"`
 		State ReviewState `json:"state"`
@@ -53,6 +59,7 @@
 
 var errInvalidReviewState = errors.New("state is wether 'accepted' nor 'declined'")
 
+// UnmarshalJSON implements the json.Unmarshal interface.
 func (rs *ReviewState) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -65,10 +72,12 @@
 	return nil
 }
 
+// MarshalJSON implements the json.Marshaler interface.
 func (it ImportTime) MarshalJSON() ([]byte, error) {
 	return json.Marshal(it.Format(common.TimeFormatMicro))
 }
 
+// Scan implements the sql.Scanner interface.
 func (it *ImportTime) Scan(x interface{}) error {
 	t, ok := x.(time.Time)
 	if !ok {
--- a/pkg/models/importbase.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/importbase.go	Sat Aug 06 02:09:57 2022 +0200
@@ -26,14 +26,19 @@
 	// CronSpec is a string containing a cron line.
 	CronSpec string
 
+	// ConfigTime represents a configuration time.
 	ConfigTime struct{ time.Time }
 
+	// ConfigDuration represents a configuration duration.
 	ConfigDuration struct{ time.Duration }
 
+	// EmailType is a type to contain an email address.
 	EmailType struct {
 		Email bool `json:"send-email,omitempty"`
 	}
 
+	// QueueConfigurationType is the common base of
+	// many configurations.
 	QueueConfigurationType struct {
 		EmailType
 		Trys      *int            `json:"trys,omitempty"`
@@ -42,6 +47,8 @@
 		Cron      *CronSpec       `json:"cron,omitempty"`
 	}
 
+	// URLType is the common base of configurations
+	// containing a password protected URL.
 	URLType struct {
 		URL      string  `json:"url"`
 		Insecure bool    `json:"insecure,omitempty"`
@@ -49,31 +56,39 @@
 		Password *string `json:"password,omitempty"`
 	}
 
+	// EmailTypeGetter tells which email type should be used.
 	EmailTypeGetter interface {
 		GetEmailType() *EmailType
 	}
 
+	// QueueConfigurationGetter tells which configuration type
+	// should be used.
 	QueueConfigurationGetter interface {
 		GetQueueConfiguration() *QueueConfigurationType
 	}
 
+	// URLTypeGetter tells which URL type should be used,
 	URLTypeGetter interface {
 		GetURLType() *URLType
 	}
 )
 
+// GetQueueConfiguration implements QueueConfigurationGetter interface.
 func (qct *QueueConfigurationType) GetQueueConfiguration() *QueueConfigurationType {
 	return qct
 }
 
+// GetURLType implements the URLTypeGetter interface.
 func (ut *URLType) GetURLType() *URLType {
 	return ut
 }
 
+// GetEmailType implements the EmailTypeGetter interface.
 func (et *EmailType) GetEmailType() *EmailType {
 	return et
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (cd *ConfigDuration) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -90,10 +105,12 @@
 	return nil
 }
 
+// MarshalJSON implements the json.Marshaler interface.
 func (cd *ConfigDuration) MarshalJSON() ([]byte, error) {
 	return json.Marshal(cd.Duration.String())
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (ct *ConfigTime) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -107,6 +124,7 @@
 	return nil
 }
 
+// MarshalJSON implements json.Marshaler interface.
 func (ct *ConfigTime) MarshalJSON() ([]byte, error) {
 	s := ct.Time.Format(common.TimeFormat)
 	return json.Marshal([]byte(s))
@@ -125,6 +143,7 @@
 	return nil
 }
 
+// MarshalAttributes stores a configuration.
 func (et *EmailType) MarshalAttributes(attrs common.Attributes) error {
 	if et.Email {
 		attrs.SetBool("email", et.Email)
@@ -132,11 +151,13 @@
 	return nil
 }
 
+// UnmarshalAttributes restores a configuration.
 func (et *EmailType) UnmarshalAttributes(attrs common.Attributes) error {
 	et.Email = attrs.Bool("email")
 	return nil
 }
 
+// MarshalAttributes stores a configuration.
 func (qct *QueueConfigurationType) MarshalAttributes(attrs common.Attributes) error {
 	if err := qct.EmailType.MarshalAttributes(attrs); err != nil {
 		return err
@@ -156,6 +177,7 @@
 	return nil
 }
 
+// UnmarshalAttributes restores a configuration.
 func (qct *QueueConfigurationType) UnmarshalAttributes(attrs common.Attributes) error {
 	if err := qct.EmailType.UnmarshalAttributes(attrs); err != nil {
 		return err
@@ -176,6 +198,7 @@
 	return nil
 }
 
+// MarshalAttributes stores a configuration.
 func (ut *URLType) MarshalAttributes(attrs common.Attributes) error {
 	attrs.Set("url", ut.URL)
 	if ut.Insecure {
@@ -190,6 +213,7 @@
 	return nil
 }
 
+// UnmarshalAttributes restores a configuration.
 func (ut *URLType) UnmarshalAttributes(attrs common.Attributes) error {
 	url, found := attrs.Get("url")
 	if !found {
--- a/pkg/models/imports.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/imports.go	Sat Aug 06 02:09:57 2022 +0200
@@ -22,11 +22,13 @@
 )
 
 type (
+	// ConfigurableURLImport is configuration with an URL.
 	ConfigurableURLImport struct {
 		URLType
 		QueueConfigurationType
 	}
 
+	// BottleneckImport is a bottleneck import.
 	BottleneckImport struct {
 		ConfigurableURLImport
 
@@ -54,6 +56,7 @@
 		ConfigurableURLImport
 	}
 
+	// WFSImport is the common base for WFS imports.
 	WFSImport struct {
 		ConfigurableURLImport
 
@@ -94,6 +97,7 @@
 		SourceOrganization string `json:"source-organization"`
 	}
 
+	// StretchImport imports a stretch.
 	StretchImport struct {
 		EmailType
 
@@ -108,6 +112,7 @@
 		Countries UniqueCountries `json:"countries"`
 	}
 
+	// SectionImport imports a section.
 	SectionImport struct {
 		EmailType
 
@@ -121,20 +126,24 @@
 		Date      Date    `json:"date-info"`
 	}
 
+	// SectionDelete deletes a section.
 	SectionDelete struct {
 		ID int64 `json:"id"`
 	}
 
+	// StretchDelete deletes a stretch.
 	StretchDelete struct {
 		ID int64 `json:"id"`
 	}
 
+	// SoundingResultDelete deletes a sounding result.
 	SoundingResultDelete struct {
-		BottleneckId string `json:"bottleneck-id"`
+		BottleneckID string `json:"bottleneck-id"`
 		Date         Date   `json:"date-info"`
 	}
 )
 
+// MarshalAttributes stores a configuration.
 func (cui *ConfigurableURLImport) MarshalAttributes(attrs common.Attributes) error {
 	if err := cui.URLType.MarshalAttributes(attrs); err != nil {
 		return err
@@ -142,6 +151,7 @@
 	return cui.QueueConfigurationType.MarshalAttributes(attrs)
 }
 
+// UnmarshalAttributes restores a configuration.
 func (cui *ConfigurableURLImport) UnmarshalAttributes(attrs common.Attributes) error {
 	if err := cui.URLType.UnmarshalAttributes(attrs); err != nil {
 		return err
@@ -149,6 +159,7 @@
 	return cui.QueueConfigurationType.UnmarshalAttributes(attrs)
 }
 
+// MarshalAttributes stores a configuration.
 func (wi *WFSImport) MarshalAttributes(attrs common.Attributes) error {
 	if err := wi.ConfigurableURLImport.MarshalAttributes(attrs); err != nil {
 		return err
@@ -160,6 +171,7 @@
 	return nil
 }
 
+// UnmarshalAttributes restores a configuration.
 func (wi *WFSImport) UnmarshalAttributes(attrs common.Attributes) error {
 	if err := wi.ConfigurableURLImport.UnmarshalAttributes(attrs); err != nil {
 		return err
@@ -175,6 +187,7 @@
 	return nil
 }
 
+// MarshalAttributes stores a configuration.
 func (bn *BottleneckImport) MarshalAttributes(attrs common.Attributes) error {
 	if err := bn.ConfigurableURLImport.MarshalAttributes(attrs); err != nil {
 		return err
@@ -183,6 +196,7 @@
 	return nil
 }
 
+// UnmarshalAttributes restores a configuration.
 func (bn *BottleneckImport) UnmarshalAttributes(attrs common.Attributes) error {
 	if err := bn.ConfigurableURLImport.UnmarshalAttributes(attrs); err != nil {
 		return err
@@ -195,6 +209,7 @@
 	return nil
 }
 
+// MarshalAttributes stores a configuration.
 func (fdi *FairwayDimensionImport) MarshalAttributes(attrs common.Attributes) error {
 	if err := fdi.WFSImport.MarshalAttributes(attrs); err != nil {
 		return err
@@ -207,6 +222,7 @@
 	return nil
 }
 
+// UnmarshalAttributes restores a configuration.
 func (fdi *FairwayDimensionImport) UnmarshalAttributes(attrs common.Attributes) error {
 	if err := fdi.WFSImport.UnmarshalAttributes(attrs); err != nil {
 		return err
@@ -239,6 +255,7 @@
 	return nil
 }
 
+// MarshalAttributes stores an configuration.
 func (sti *StretchImport) MarshalAttributes(attrs common.Attributes) error {
 	if err := sti.EmailType.MarshalAttributes(attrs); err != nil {
 		return err
@@ -263,6 +280,7 @@
 	return nil
 }
 
+// UnmarshalAttributes restores a configuration.
 func (sti *StretchImport) UnmarshalAttributes(attrs common.Attributes) error {
 	if err := sti.EmailType.UnmarshalAttributes(attrs); err != nil {
 		return err
@@ -321,6 +339,7 @@
 	return nil
 }
 
+// MarshalAttributes stores a configuration.
 func (seci *SectionImport) MarshalAttributes(attrs common.Attributes) error {
 	if err := seci.EmailType.MarshalAttributes(attrs); err != nil {
 		return err
@@ -338,6 +357,7 @@
 	return nil
 }
 
+// UnmarshalAttributes restores a configuration.
 func (seci *SectionImport) UnmarshalAttributes(attrs common.Attributes) error {
 	if err := seci.EmailType.UnmarshalAttributes(attrs); err != nil {
 		return err
--- a/pkg/models/intservices.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/intservices.go	Sat Aug 06 02:09:57 2022 +0200
@@ -25,9 +25,12 @@
 	"gemma.intevation.de/gemma/pkg/log"
 )
 
+// DatabaseScheme is the schema used for the data
+// accessible for the waterway user.
 const DatabaseScheme = "waterway"
 
 type (
+	// IntEntry repreents a published layer in the GeoServer.
 	IntEntry struct {
 		Schema           string  `json:"schema"`
 		Name             string  `json:"name"`
@@ -41,11 +44,14 @@
 		WMSTEndAttribute *string `json:"wmst-end-attribute"`
 	}
 
+	// LayerGroup is a layer group in a published GeoServer layer.
 	LayerGroup struct {
 		Name   string   `json:"name"`
 		Layers []string `json:"layers"`
 	}
 
+	// IntServices are the internal services published by
+	// the GeoServer.
 	IntServices struct {
 		mu          sync.Mutex
 		entries     []IntEntry
@@ -87,8 +93,11 @@
 WHERE name = $2 AND schema = $3`
 )
 
+// InternalServices is the list of the internal services
+// managed by the Gemma server.
 var InternalServices = &IntServices{}
 
+// LoadStyle a style for a given entry.
 func (e *IntEntry) LoadStyle() ([]byte, error) {
 	var style []byte
 	ctx := context.Background()
@@ -102,6 +111,7 @@
 	return style, err
 }
 
+// UpdateInternalStyle updates a style in the database.
 func UpdateInternalStyle(req *http.Request, name string, style []byte) error {
 	return auth.RunAsSessionUser(req, func(conn *sql.Conn) error {
 		_, err := conn.ExecContext(
@@ -114,6 +124,7 @@
 	})
 }
 
+// LayerGroups returns the list of layer groups from the database.
 func (ps *IntServices) LayerGroups() []LayerGroup {
 	ps.mu.Lock()
 	defer ps.mu.Unlock()
@@ -136,6 +147,7 @@
 	return groups
 }
 
+// Find looks for an internal service with a given name.
 func (ps *IntServices) Find(name string) (string, bool) {
 	ps.mu.Lock()
 	defer ps.mu.Unlock()
@@ -228,6 +240,7 @@
 	return auth.RunAllAs(ctx, "sys_admin", entries, groups)
 }
 
+// Invalidate invalidates the list on internal services.
 func (ps *IntServices) Invalidate() {
 	ps.mu.Lock()
 	ps.entries = nil
@@ -235,16 +248,27 @@
 	ps.mu.Unlock()
 }
 
-func InternalAll(IntEntry) bool        { return true }
-func IntWMS(entry IntEntry) bool       { return entry.WMS }
-func IntWFS(entry IntEntry) bool       { return entry.WFS }
-func IntSQLView(entry IntEntry) bool   { return entry.SQL != nil }
+// InternalAll passes all entries.
+func InternalAll(IntEntry) bool { return true }
+
+// IntWMS passes only WMS entries.
+func IntWMS(entry IntEntry) bool { return entry.WMS }
+
+// IntWFS passes only WFS entries.
+func IntWFS(entry IntEntry) bool { return entry.WFS }
+
+// IntSQLView passes only entries with a SQL view.
+func IntSQLView(entry IntEntry) bool { return entry.SQL != nil }
+
+// IntWithStyle passes only entries with a style.
 func IntWithStyle(entry IntEntry) bool { return entry.Style }
 
+// IntByName passes only entries with a given name.
 func IntByName(name string) func(IntEntry) bool {
 	return func(entry IntEntry) bool { return entry.Name == name }
 }
 
+// IntAnd filters internal entries that match all conditions.
 func IntAnd(accept ...func(IntEntry) bool) func(IntEntry) bool {
 	return func(entry IntEntry) bool {
 		for _, a := range accept {
@@ -256,6 +280,8 @@
 	}
 }
 
+// Filter returns a list of internal services that match the
+// given condition.
 func (ps *IntServices) Filter(accept func(IntEntry) bool) []IntEntry {
 	ps.mu.Lock()
 	defer ps.mu.Unlock()
--- a/pkg/models/isrs.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/isrs.go	Sat Aug 06 02:09:57 2022 +0200
@@ -30,6 +30,7 @@
 	Hectometre     int
 }
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (isrs *Isrs) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -43,6 +44,7 @@
 	return nil
 }
 
+// Less orders two Isrs codes.
 func (isrs *Isrs) Less(other *Isrs) bool {
 	if isrs.CountryCode < other.CountryCode {
 		return true
@@ -74,6 +76,7 @@
 	return false
 }
 
+// MarshalJSON implements the json.Marshaler interface.
 func (isrs *Isrs) MarshalJSON() ([]byte, error) {
 	if isrs == nil {
 		return nil, nil
--- a/pkg/models/reproject.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/reproject.go	Sat Aug 06 02:09:57 2022 +0200
@@ -22,12 +22,15 @@
 SELECT ST_X(p), ST_Y(p)
 FROM ST_Transform(ST_SetSRID(ST_MakePoint($1, $2), $3::integer), $4::integer) AS p`
 
+// Reprojector re-projects point data from one coord system
+// to another.
 type Reprojector struct {
 	stmt     *sql.Stmt
 	FromEPSG uint32
 	ToEPSG   uint32
 }
 
+// NewReprojector creates a reprojector.
 func NewReprojector(
 	ctx context.Context,
 	conn *sql.Conn,
@@ -44,6 +47,7 @@
 	}, nil
 }
 
+// Close closes the reprojector.
 func (r *Reprojector) Close() error {
 	if s := r.stmt; s != nil {
 		r.stmt = nil
@@ -52,6 +56,7 @@
 	return nil
 }
 
+// Reproject performs the re-projection for a given point.
 func (r *Reprojector) Reproject(
 	ctx context.Context,
 	x, y float64,
--- a/pkg/models/search.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/search.go	Sat Aug 06 02:09:57 2022 +0200
@@ -17,9 +17,8 @@
 	"time"
 )
 
-type (
-	SearchRequest struct {
-		SearchString string `json:"string"`
-		SearchTime *time.Time `json:"time"`
-	}
-)
+// SearchRequest is the model for a search.
+type SearchRequest struct {
+	SearchString string     `json:"string"`
+	SearchTime   *time.Time `json:"time"`
+}
--- a/pkg/models/sr.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/sr.go	Sat Aug 06 02:09:57 2022 +0200
@@ -25,26 +25,31 @@
 	"gemma.intevation.de/gemma/pkg/common"
 )
 
+// SurveyType is the type of a survey.
 type SurveyType string
 
 const (
-	SurveyTypeMultiBeam  = SurveyType("multi")
+	// SurveyTypeMultiBeam is multi beam scan.
+	SurveyTypeMultiBeam = SurveyType("multi")
+	// SurveyTypeSingleBeam is a single beam scan.
 	SurveyTypeSingleBeam = SurveyType("single")
-	SurveyTypeMarking    = SurveyType("marking")
+	// SurveyTypeMarking is a scan from a marking vessel.
+	SurveyTypeMarking = SurveyType("marking")
 )
 
-type (
-	SoundingResultMeta struct {
-		Date           Date       `json:"date"`
-		Bottleneck     string     `json:"bottleneck"`
-		EPSG           uint       `json:"epsg"`
-		DepthReference string     `json:"depth-reference"`
-		SingleBeam     *bool      `json:"single-beam,omitempty"` // kept in for compat!
-		SurveyType     SurveyType `json:"survey-type,omitempty"`
-		NegateZ        bool       `json:"negate-z,omitempty"`
-	}
-)
+// SoundingResultMeta is the JSON structure of
+// a metadata.json file.
+type SoundingResultMeta struct {
+	Date           Date       `json:"date"`
+	Bottleneck     string     `json:"bottleneck"`
+	EPSG           uint       `json:"epsg"`
+	DepthReference string     `json:"depth-reference"`
+	SingleBeam     *bool      `json:"single-beam,omitempty"` // kept in for compat!
+	SurveyType     SurveyType `json:"survey-type,omitempty"`
+	NegateZ        bool       `json:"negate-z,omitempty"`
+}
 
+// UnmarshalJSON implements the json.Unmarshaler interface.
 func (st *SurveyType) UnmarshalJSON(data []byte) error {
 	var s string
 	if err := json.Unmarshal(data, &s); err != nil {
@@ -79,6 +84,8 @@
 WHERE bn.bottleneck_id = $1 AND sr.date_info = $2`
 )
 
+// Decode deserializes a metadata.json and checks if
+// the data fields met the needed constraints.
 func (m *SoundingResultMeta) Decode(r io.Reader) error {
 	if err := json.NewDecoder(r).Decode(m); err != nil {
 		return err
@@ -113,6 +120,8 @@
 	return nil
 }
 
+// Validate validates the metadata.json data against the
+// constraints in the database.
 func (m *SoundingResultMeta) Validate(ctx context.Context, conn *sql.Conn) []error {
 
 	var errs []error
--- a/pkg/models/surveys.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/models/surveys.go	Sat Aug 06 02:09:57 2022 +0200
@@ -13,13 +13,12 @@
 
 package models
 
-type (
-	Survey struct {
-		BottleneckID    string     `json:"bottleneck_id"`
-		DateInfo        string     `json:"date_info"`
-		DepthReference  string     `json:"depth_reference"`
-		ReferenceGauge  string     `json:"gauge_objname"`
-		SurveyType      SurveyType `json:"survey_type"`
-		WaterLevelValue *int64     `json:"waterlevel_value,omitempty"`
-	}
-)
+// Survey contains the relevant data about a survey.
+type Survey struct {
+	BottleneckID    string     `json:"bottleneck_id"`
+	DateInfo        string     `json:"date_info"`
+	DepthReference  string     `json:"depth_reference"`
+	ReferenceGauge  string     `json:"gauge_objname"`
+	SurveyType      SurveyType `json:"survey_type"`
+	WaterLevelValue *int64     `json:"waterlevel_value,omitempty"`
+}
--- a/pkg/wkb/data.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/wkb/data.go	Sat Aug 06 02:09:57 2022 +0200
@@ -21,15 +21,20 @@
 )
 
 type (
+	// PointGeom is a 2D point,
 	PointGeom struct {
 		X float64
 		Y float64
 	}
-	LinearRingGeom   []PointGeom
-	PolygonGeom      []LinearRingGeom
+	// LinearRingGeom is a ring of points.
+	LinearRingGeom []PointGeom
+	// PolygonGeom is a polygon.
+	PolygonGeom []LinearRingGeom
+	// MultiPolygonGeom is a multi polygon.
 	MultiPolygonGeom []PolygonGeom
 )
 
+// AsWKB serializes a multi polygon as WKB.
 func (mpg MultiPolygonGeom) AsWKB() []byte {
 
 	size := 1 + 4 + 4
@@ -65,6 +70,7 @@
 	return buf.Bytes()
 }
 
+// FromWKB deserializes a multi polygon from WKB.
 func (mpg *MultiPolygonGeom) FromWKB(data []byte) error {
 	r := bytes.NewReader(data)
 
@@ -152,6 +158,7 @@
 	return nil
 }
 
+// CCW tells if the ring is oriented counter clock wise.
 func (lr LinearRingGeom) CCW() bool {
 	var sum float64
 	for i, v1 := range lr {
@@ -161,6 +168,7 @@
 	return sum > 0
 }
 
+// Reverse changes the orientation of the ring.
 func (lr LinearRingGeom) Reverse() {
 	for i, j := 0, len(lr)-1; i < j; i, j = i+1, j-1 {
 		lr[i], lr[j] = lr[j], lr[i]
--- a/pkg/xlsx/templater.go	Sat Aug 06 00:46:21 2022 +0200
+++ b/pkg/xlsx/templater.go	Sat Aug 06 02:09:57 2022 +0200
@@ -32,6 +32,9 @@
 	"gemma.intevation.de/gemma/pkg/log"
 )
 
+// Action defines what to do.
+// It can contain sub actions.
+// Depending on the type a specific action is performed.
 type Action struct {
 	Type        string    `yaml:"type"`
 	Actions     []*Action `yaml:"actions"`
@@ -102,6 +105,7 @@
 	return a.x1 <= x && x <= a.x2 && a.y1 <= y && y <= a.y2
 }
 
+// ActionFromFile reads an action from file.
 func ActionFromFile(filename string) (*Action, error) {
 	f, err := os.Open(filename)
 	if err != nil {
@@ -111,12 +115,15 @@
 	return ActionFromReader(f)
 }
 
+// ActionFromReader reads an action from a reader.
 func ActionFromReader(r io.Reader) (*Action, error) {
 	action := new(Action)
 	err := yaml.NewDecoder(bufio.NewReader(r)).Decode(action)
 	return action, err
 }
 
+// Execute executes an action in a given database transaction
+// an a template as a basis.
 func (a *Action) Execute(
 	ctx context.Context,
 	tx *sql.Tx,