# HG changeset patch # User Sascha L. Teichmann # Date 1659744597 -7200 # Node ID 1222b777f51f0572d252bcb2ca24ce22811073de # Parent 9967a78e43f45548e58938cbabfe8bab33bec6b4 Made golint finally happy. diff -r 9967a78e43f4 -r 1222b777f51f pkg/config/config.go --- 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 diff -r 9967a78e43f4 -r 1222b777f51f pkg/controllers/surveys.go --- 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 } diff -r 9967a78e43f4 -r 1222b777f51f pkg/imports/fm.go --- 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() { diff -r 9967a78e43f4 -r 1222b777f51f pkg/imports/modelconvert.go --- 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, } }, diff -r 9967a78e43f4 -r 1222b777f51f pkg/imports/queue.go --- 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 diff -r 9967a78e43f4 -r 1222b777f51f pkg/imports/report.go --- 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, diff -r 9967a78e43f4 -r 1222b777f51f pkg/imports/statsupdate.go --- 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, diff -r 9967a78e43f4 -r 1222b777f51f pkg/imports/wfsjob.go --- 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{}, diff -r 9967a78e43f4 -r 1222b777f51f pkg/log/log.go --- 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) diff -r 9967a78e43f4 -r 1222b777f51f pkg/mesh/classbreaks.go --- 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 diff -r 9967a78e43f4 -r 1222b777f51f pkg/mesh/loader.go --- 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 { diff -r 9967a78e43f4 -r 1222b777f51f pkg/mesh/polygon.go --- 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 diff -r 9967a78e43f4 -r 1222b777f51f pkg/mesh/raster.go --- 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() diff -r 9967a78e43f4 -r 1222b777f51f pkg/mesh/tin.go --- 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) diff -r 9967a78e43f4 -r 1222b777f51f pkg/mesh/triangulation.go --- 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} diff -r 9967a78e43f4 -r 1222b777f51f pkg/mesh/vertex.go --- 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} diff -r 9967a78e43f4 -r 1222b777f51f pkg/middleware/nosniff.go --- 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") diff -r 9967a78e43f4 -r 1222b777f51f pkg/misc/http.go --- 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) diff -r 9967a78e43f4 -r 1222b777f51f pkg/misc/mail.go --- 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, diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/common.go --- 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)) } diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/cross.go --- 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 diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/diff.go --- 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"` diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/extservices.go --- 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() diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/import.go --- 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 { diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/importbase.go --- 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 { diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/imports.go --- 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 diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/intservices.go --- 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() diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/isrs.go --- 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 diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/reproject.go --- 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, diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/search.go --- 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"` +} diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/sr.go --- 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 diff -r 9967a78e43f4 -r 1222b777f51f pkg/models/surveys.go --- 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"` +} diff -r 9967a78e43f4 -r 1222b777f51f pkg/wkb/data.go --- 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] diff -r 9967a78e43f4 -r 1222b777f51f pkg/xlsx/templater.go --- 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,