diff pkg/imports/fm_boycar.go @ 4913:8c1a3d5e3962 fairway-marks-import

Add import for fairway marks of type BOYCAR
author Tom Gottfried <tom@intevation.de>
date Mon, 10 Feb 2020 18:47:27 +0100
parents
children b86ce7fc4da3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkg/imports/fm_boycar.go	Mon Feb 10 18:47:27 2020 +0100
@@ -0,0 +1,225 @@
+// This is Free Software under GNU Affero General Public License v >= 3.0
+// without warranty, see README.md and license for details.
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// License-Filename: LICENSES/AGPL-3.0.txt
+//
+// Copyright (C) 2020 by via donau
+//   – Österreichische Wasserstraßen-Gesellschaft mbH
+// Software engineering by Intevation GmbH
+//
+// Author(s):
+//  * Tom Gottfried <tom.gottfried@intevation.de>
+
+package imports
+
+import (
+	"context"
+	"database/sql"
+	"time"
+
+	"gemma.intevation.de/gemma/pkg/pgxutils"
+)
+
+// Boycar is an import job to import
+// fairway marks of type BOYCAR in form of point geometries
+// and attribute data from a WFS service.
+type Boycar struct {
+	FairwayMarks
+}
+
+// Description gives a short info about relevant facts of this import.
+func (boycar *Boycar) Description() (string, error) {
+	return boycar.URL + "|" + boycar.FeatureType, nil
+}
+
+// BOYCARJobKind is the import queue type identifier.
+const BOYCARJobKind JobKind = "fm_boycar"
+
+type boycarJobCreator struct{}
+
+func init() {
+	RegisterJobCreator(BOYCARJobKind, boycarJobCreator{})
+}
+
+func (boycarJobCreator) Description() string { return "fairway marks boycar" }
+
+func (boycarJobCreator) AutoAccept() bool { return true }
+
+func (boycarJobCreator) Create() Job { return new(Boycar) }
+
+func (boycarJobCreator) Depends() [2][]string {
+	return [2][]string{
+		{"fairway_marks_boycar"},
+		{},
+	}
+}
+
+// StageDone is a NOP for fairway marks imports.
+func (boycarJobCreator) StageDone(context.Context, *sql.Tx, int64) error {
+	return nil
+}
+
+// CleanUp for fairway marks imports is a NOP.
+func (*Boycar) CleanUp() error { return nil }
+
+type boycarProperties struct {
+	fairwayMarksProperties
+	Colour *string `json:"hydro_colour"`
+	Colpat *string `json:"hydro_colpat"`
+	Conrad *int    `json:"hydro_conrad"`
+	Marsys *int    `json:"hydro_marsys"`
+	Boyshp *int    `json:"hydro_boyshp"`
+}
+
+type boycarFeaturetype struct {
+	geom  pointSlice
+	props *boycarProperties
+}
+
+const (
+	insertBOYCARSQL = `
+with a as (
+  select users.current_user_area_utm() AS a
+)
+INSERT INTO waterway.fairway_marks_boycar (
+  geom,
+  datsta,
+  datend,
+  persta,
+  perend,
+  objnam,
+  nobjnm,
+  inform,
+  ninfom,
+  scamin,
+  picrep,
+  txtdsc,
+  sordat,
+  sorind,
+  colour,
+  colpat,
+  conrad,
+  marsys,
+  boyshp
+)
+SELECT newfm, $3, $4, $5, $6, $7, $8, $9,
+    $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20
+  FROM ST_Transform(ST_GeomFromWKB($1, $2::integer), 4326) AS newfm (newfm)
+  WHERE pg_has_role('sys_admin', 'MEMBER')
+    OR ST_Intersects((select a from a),
+      ST_Transform(newfm, (select ST_SRID(a) from a)))
+ON CONFLICT (
+  CAST((geom,
+      datsta, datend, persta, perend, objnam, nobjnm, inform, ninfom,
+      scamin, picrep, txtdsc, sordat, sorind,
+      0, colour, colpat, conrad, marsys, boyshp
+    ) AS waterway.fairway_marks_boycar)
+  )
+  DO NOTHING
+RETURNING id
+`
+)
+
+// Do executes the actual import.
+func (fm *Boycar) Do(
+	ctx context.Context,
+	importID int64,
+	conn *sql.Conn,
+	feedback Feedback,
+) (interface{}, error) {
+
+	start := time.Now()
+
+	feedback.Info("Import fairway marks of type BOYCAR")
+
+	fms, epsg, err := getFMFeatures(
+		feedback,
+		fm.FairwayMarks,
+		func() interface{} { return new(boycarProperties) },
+		func(p pointSlice, props interface{}) interface{} {
+			return &boycarFeaturetype{p, props.(*boycarProperties)}
+		},
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	tx, err := conn.BeginTx(ctx, nil)
+	if err != nil {
+		return nil, err
+	}
+	defer tx.Rollback()
+
+	insertStmt, err := tx.PrepareContext(ctx, insertBOYCARSQL)
+	if err != nil {
+		return nil, err
+	}
+	defer insertStmt.Close()
+
+	savepoint := Savepoint(ctx, tx, "feature")
+
+	var (
+		outsideOrDup int
+		features     int
+	)
+	for _, fm := range fms {
+
+		f := fm.(*boycarFeaturetype)
+
+		var fmid int64
+		err := savepoint(func() error {
+			err := insertStmt.QueryRowContext(
+				ctx,
+				f.geom.asWKB(),
+				epsg,
+				f.props.Datsta,
+				f.props.Datend,
+				f.props.Persta,
+				f.props.Perend,
+				f.props.Objnam,
+				f.props.Nobjnm,
+				f.props.Inform,
+				f.props.Ninfom,
+				f.props.Scamin,
+				f.props.Picrep,
+				f.props.Txtdsc,
+				f.props.Sordat,
+				f.props.Sorind,
+				f.props.Colour,
+				f.props.Colpat,
+				f.props.Conrad,
+				f.props.Marsys,
+				f.props.Boyshp,
+			).Scan(&fmid)
+			return err
+		})
+		switch {
+		case err == sql.ErrNoRows:
+			outsideOrDup++
+			// ignore -> filtered by responsibility_areas
+		case err != nil:
+			feedback.Error(pgxutils.ReadableError{Err: err}.Error())
+		default:
+			features++
+		}
+	}
+
+	if outsideOrDup > 0 {
+		feedback.Info(
+			"Features outside responsibility area and duplicates: %d",
+			outsideOrDup)
+	}
+
+	if features == 0 {
+		err := UnchangedError("no valid new features found")
+		return nil, err
+	}
+
+	if err = tx.Commit(); err == nil {
+		feedback.Info("Storing %d features took %s",
+			features, time.Since(start))
+	}
+
+	return nil, err
+}