changeset 4962:1b309a8e7673 fairway-marks-import

Distinguish more clearly between BCNLAT HYDRO and IENC features This allows importing them from different sources while keeping the history of data intact. Additionally, storing them in different tables also allows to have different attributes (here only dirimp via an m:n-table) and different constraints (currently not implemented) according to the IENC feature catalogue. Since both new tables inherit from a table with the same name as the old table, all entries still can be accessed via a table of the same name. Thus, no changes to GeoServer layers are necessary. ToDo: solve layout problems in the client SPA.
author Tom Gottfried <tom@intevation.de>
date Thu, 27 Feb 2020 21:05:09 +0100
parents 67d78b74fe43
children 1e47ba2a58f2
files client/src/components/importconfiguration/types/Fairwaymarks.vue client/src/store/importschedule.js pkg/controllers/routes.go pkg/imports/fm.go pkg/imports/modelconvert.go schema/gemma.sql schema/updates/1406/01.distinguish_bcnlat_hydro_ienc.sql schema/version.sql
diffstat 8 files changed, 208 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/importconfiguration/types/Fairwaymarks.vue	Wed Feb 26 19:28:21 2020 +0100
+++ b/client/src/components/importconfiguration/types/Fairwaymarks.vue	Thu Feb 27 21:05:09 2020 +0100
@@ -125,7 +125,8 @@
   },
   FAIRWAYMARKS: {
     BCNISD: "Beacon, isolated danger (MARITIME/Hydro feature)",
-    BCNLAT: "Beacon, lateral (MARITIME/Hydro feature)",
+    BCNLAT_hydro: "Beacon, lateral (MARITIME/Hydro feature)",
+    bcnlat_ienc: "Beacon, lateral (IENC feature)",
     BOYCAR: "Buoy, cardinal (MARITIME/Hydro feature)",
     BOYISD: "Buoy, isolated danger (MARITIME/Hydro feature)",
     BOYLAT: "Buoy, lateral (MARITIME/Hydro feature)",
--- a/client/src/store/importschedule.js	Wed Feb 26 19:28:21 2020 +0100
+++ b/client/src/store/importschedule.js	Thu Feb 27 21:05:09 2020 +0100
@@ -62,7 +62,8 @@
 
 const FAIRWAYMARKKINDS = {
   fm_bcnisd: "BCNISD",
-  fm_bcnlat: "BCNLAT",
+  fm_bcnlat_hydro: "BCNLAT_hydro",
+  fm_bcnlat_ienc: "bcnlat_ienc",
   fm_boycar: "BOYCAR",
   fm_boyisd: "BOYISD",
   fm_boylat: "BOYLAT",
--- a/pkg/controllers/routes.go	Wed Feb 26 19:28:21 2020 +0100
+++ b/pkg/controllers/routes.go	Thu Feb 27 21:05:09 2020 +0100
@@ -243,7 +243,8 @@
 		"bn", "gm", "fa", "wx", "wa",
 		"wg", "dmv", "fd", "dma",
 		"sec", "dsec", "dst", "dsr",
-		"fm_bcnlat",
+		"fm_bcnlat_hydro",
+		"fm_bcnlat_ienc",
 		"fm_boycar",
 		"fm_boylat",
 		"fm_boysaw",
--- a/pkg/imports/fm.go	Wed Feb 26 19:28:21 2020 +0100
+++ b/pkg/imports/fm.go	Thu Feb 27 21:05:09 2020 +0100
@@ -42,13 +42,21 @@
 
 	bcnlatProperties struct {
 		fairwayMarksProperties
-		Colour      *string `json:"hydro_colour"`
-		Colpat      *string `json:"hydro_colpat"`
-		Condtn      *int    `json:"hydro_condtn"`
-		Bcnshp      *int    `json:"hydro_bcnshp"`
-		HydroCatlam *int64  `json:"hydro_catlam,omitempty"`
-		IENCCatlam  *int64  `json:"ienc_catlam,omitempty"`
-		Dirimp      *string `json:"ienc_dirimp,omitempty"`
+		Colour *string `json:"hydro_colour"`
+		Colpat *string `json:"hydro_colpat"`
+		Condtn *int    `json:"hydro_condtn"`
+		Bcnshp *int    `json:"hydro_bcnshp"`
+	}
+
+	bcnlatHydroProperties struct {
+		bcnlatProperties
+		Catlam *int64 `json:"hydro_catlam"`
+	}
+
+	bcnlatIencProperties struct {
+		bcnlatProperties
+		Catlam *int64  `json:"ienc_catlam"`
+		Dirimp *string `json:"ienc_dirimp"`
 	}
 
 	boylatProperties struct {
@@ -155,32 +163,48 @@
 )
 
 const (
-	BCNLATJobKind JobKind = "fm_bcnlat"
-	BOYLATJobKind JobKind = "fm_boylat"
-	BOYCARJobKind JobKind = "fm_boycar"
-	BOYSAWJobKind JobKind = "fm_boysaw"
-	BOYSPPJobKind JobKind = "fm_boyspp"
-	DAYMARJobKind JobKind = "fm_daymar"
-	LIGHTSJobKind JobKind = "fm_lights"
-	NOTMRKJobKind JobKind = "fm_notmrk"
-	RTPBCNJobKind JobKind = "fm_rtpbcn"
-	TOPMARJobKind JobKind = "fm_topmar"
+	BCNLATHYDROJobKind JobKind = "fm_bcnlat_hydro"
+	BCNLATIENCJobKind  JobKind = "fm_bcnlat_ienc"
+	BOYLATJobKind      JobKind = "fm_boylat"
+	BOYCARJobKind      JobKind = "fm_boycar"
+	BOYSAWJobKind      JobKind = "fm_boysaw"
+	BOYSPPJobKind      JobKind = "fm_boyspp"
+	DAYMARJobKind      JobKind = "fm_daymar"
+	LIGHTSJobKind      JobKind = "fm_lights"
+	NOTMRKJobKind      JobKind = "fm_notmrk"
+	RTPBCNJobKind      JobKind = "fm_rtpbcn"
+	TOPMARJobKind      JobKind = "fm_topmar"
 )
 
 func init() {
-	RegisterJobCreator(BCNLATJobKind,
+	RegisterJobCreator(BCNLATHYDROJobKind,
 		&PointWFSJobCreator{
-			description: "fairway marks bcnlat",
-			depends:     [2][]string{{"fairway_marks_bcnlat"}, {}},
+			description: "fairway marks bcnlat (HYDRO)",
+			depends:     [2][]string{{"fairway_marks_bcnlat_hydro"}, {}},
 			newConsumer: newSQLConsumer(
 				prepareStmnts(
-					createInsertFMSQL("bcnlat",
+					createInsertFMSQL("bcnlat_hydro",
+						"colour", "colpat", "condtn", "bcnshp", "catlam"),
+				),
+				consumeBCNLATHydro,
+				createInvalidation("bcnlat_hydro"),
+				func() interface{} { return new(bcnlatHydroProperties) },
+			),
+		})
+
+	RegisterJobCreator(BCNLATIENCJobKind,
+		&PointWFSJobCreator{
+			description: "fairway marks bcnlat (IENC)",
+			depends:     [2][]string{{"fairway_marks_bcnlat_ienc"}, {}},
+			newConsumer: newSQLConsumer(
+				prepareStmnts(
+					createInsertFMSQL("bcnlat_ienc",
 						"colour", "colpat", "condtn", "bcnshp", "catlam"),
 					insertBcnlatDirimpSQL,
 				),
-				consumeBCNLAT,
-				createInvalidation("bcnlat"),
-				func() interface{} { return new(bcnlatProperties) },
+				consumeBCNLATIenc,
+				createInvalidation("bcnlat_ienc"),
+				func() interface{} { return new(bcnlatIencProperties) },
 			),
 		})
 
@@ -499,15 +523,13 @@
 	}
 }
 
-func consumeBCNLAT(
+func consumeBCNLATHydro(
 	spc *SQLPointConsumer,
 	points pointSlice,
 	properties interface{},
 	epsg int,
 ) error {
-	props := properties.(*bcnlatProperties)
-
-	catlam := coalesceInt64(props.HydroCatlam, props.IENCCatlam)
+	props := properties.(*bcnlatHydroProperties)
 
 	var fmid int64
 	err := spc.savepoint(func() error {
@@ -532,7 +554,52 @@
 			props.Colpat,
 			props.Condtn,
 			props.Bcnshp,
-			catlam,
+			props.Catlam,
+		).Scan(&fmid)
+	})
+	switch {
+	case err == sql.ErrNoRows:
+		return ErrFeatureDuplicated
+		// ignore -> filtered by responsibility area or a duplicate
+	case err != nil:
+		spc.feedback.Error(pgxutils.ReadableError{Err: err}.Error())
+		return ErrFeatureIgnored
+	}
+	return nil
+}
+
+func consumeBCNLATIenc(
+	spc *SQLPointConsumer,
+	points pointSlice,
+	properties interface{},
+	epsg int,
+) error {
+	props := properties.(*bcnlatIencProperties)
+
+	var fmid int64
+	err := spc.savepoint(func() error {
+		return spc.stmts[0].QueryRowContext(
+			spc.ctx,
+			points.asWKB(),
+			epsg,
+			props.Datsta,
+			props.Datend,
+			props.Persta,
+			props.Perend,
+			props.Objnam,
+			props.Nobjnm,
+			props.Inform,
+			props.Ninfom,
+			props.Scamin,
+			props.Picrep,
+			props.Txtdsc,
+			props.Sordat,
+			props.Sorind,
+			props.Colour,
+			props.Colpat,
+			props.Condtn,
+			props.Bcnshp,
+			props.Catlam,
 		).Scan(&fmid)
 	})
 	switch {
--- a/pkg/imports/modelconvert.go	Wed Feb 26 19:28:21 2020 +0100
+++ b/pkg/imports/modelconvert.go	Thu Feb 27 21:05:09 2020 +0100
@@ -18,30 +18,31 @@
 )
 
 var kindToImportModel = map[JobKind]func() interface{}{
-	BNJobKind:     func() interface{} { return new(models.BottleneckImport) },
-	GMJobKind:     func() interface{} { return new(models.GaugeMeasurementImport) },
-	FAJobKind:     func() interface{} { return new(models.FairwayAvailabilityImport) },
-	WXJobKind:     func() interface{} { return new(models.WaterwayAxisImport) },
-	WAJobKind:     func() interface{} { return new(models.WaterwayAreaImport) },
-	WGJobKind:     func() interface{} { return new(models.WaterwayGaugeImport) },
-	DMVJobKind:    func() interface{} { return new(models.DistanceMarksVirtualImport) },
-	FDJobKind:     func() interface{} { return new(models.FairwayDimensionImport) },
-	DMAJobKind:    func() interface{} { return new(models.DistanceMarksAshoreImport) },
-	BCNLATJobKind: func() interface{} { return FindJobCreator(BCNLATJobKind).Create() },
-	BOYCARJobKind: func() interface{} { return FindJobCreator(BOYCARJobKind).Create() },
-	BOYLATJobKind: func() interface{} { return FindJobCreator(BOYLATJobKind).Create() },
-	BOYSAWJobKind: func() interface{} { return FindJobCreator(BOYSAWJobKind).Create() },
-	BOYSPPJobKind: func() interface{} { return FindJobCreator(BOYSPPJobKind).Create() },
-	DAYMARJobKind: func() interface{} { return FindJobCreator(DAYMARJobKind).Create() },
-	LIGHTSJobKind: func() interface{} { return FindJobCreator(LIGHTSJobKind).Create() },
-	RTPBCNJobKind: func() interface{} { return FindJobCreator(RTPBCNJobKind).Create() },
-	TOPMARJobKind: func() interface{} { return FindJobCreator(TOPMARJobKind).Create() },
-	NOTMRKJobKind: func() interface{} { return FindJobCreator(NOTMRKJobKind).Create() },
-	STJobKind:     func() interface{} { return new(models.StretchImport) },
-	SECJobKind:    func() interface{} { return new(models.SectionImport) },
-	DSECJobKind:   func() interface{} { return new(models.SectionDelete) },
-	DSTJobKind:    func() interface{} { return new(models.StretchDelete) },
-	DSRJobKind:    func() interface{} { return new(models.SoundingResultDelete) },
+	BNJobKind:          func() interface{} { return new(models.BottleneckImport) },
+	GMJobKind:          func() interface{} { return new(models.GaugeMeasurementImport) },
+	FAJobKind:          func() interface{} { return new(models.FairwayAvailabilityImport) },
+	WXJobKind:          func() interface{} { return new(models.WaterwayAxisImport) },
+	WAJobKind:          func() interface{} { return new(models.WaterwayAreaImport) },
+	WGJobKind:          func() interface{} { return new(models.WaterwayGaugeImport) },
+	DMVJobKind:         func() interface{} { return new(models.DistanceMarksVirtualImport) },
+	FDJobKind:          func() interface{} { return new(models.FairwayDimensionImport) },
+	DMAJobKind:         func() interface{} { return new(models.DistanceMarksAshoreImport) },
+	BCNLATHYDROJobKind: func() interface{} { return FindJobCreator(BCNLATHYDROJobKind).Create() },
+	BCNLATIENCJobKind:  func() interface{} { return FindJobCreator(BCNLATIENCJobKind).Create() },
+	BOYCARJobKind:      func() interface{} { return FindJobCreator(BOYCARJobKind).Create() },
+	BOYLATJobKind:      func() interface{} { return FindJobCreator(BOYLATJobKind).Create() },
+	BOYSAWJobKind:      func() interface{} { return FindJobCreator(BOYSAWJobKind).Create() },
+	BOYSPPJobKind:      func() interface{} { return FindJobCreator(BOYSPPJobKind).Create() },
+	DAYMARJobKind:      func() interface{} { return FindJobCreator(DAYMARJobKind).Create() },
+	LIGHTSJobKind:      func() interface{} { return FindJobCreator(LIGHTSJobKind).Create() },
+	RTPBCNJobKind:      func() interface{} { return FindJobCreator(RTPBCNJobKind).Create() },
+	TOPMARJobKind:      func() interface{} { return FindJobCreator(TOPMARJobKind).Create() },
+	NOTMRKJobKind:      func() interface{} { return FindJobCreator(NOTMRKJobKind).Create() },
+	STJobKind:          func() interface{} { return new(models.StretchImport) },
+	SECJobKind:         func() interface{} { return new(models.SectionImport) },
+	DSECJobKind:        func() interface{} { return new(models.SectionDelete) },
+	DSTJobKind:         func() interface{} { return new(models.StretchDelete) },
+	DSRJobKind:         func() interface{} { return new(models.SoundingResultDelete) },
 }
 
 // ImportModelForJobKind returns the constructor function to
--- a/schema/gemma.sql	Wed Feb 26 19:28:21 2020 +0100
+++ b/schema/gemma.sql	Thu Feb 27 21:05:09 2020 +0100
@@ -879,26 +879,43 @@
 
     -- Additional attributes for IENC features BCNLAT/bcnlat
     CREATE TABLE fairway_marks_bcnlat (
-        id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
         colour varchar,
         colpat varchar,
         condtn int,
         bcnshp int,
         catlam int
     ) INHERITS (fairway_marks)
+
+    CREATE TABLE fairway_marks_bcnlat_hydro (
+        id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
+    ) INHERITS (fairway_marks_bcnlat)
     -- Prevent identical entries using composite type comparison
     -- (i.e. considering two NULL values in a field equal):
-    CREATE UNIQUE INDEX fairway_marks_bcnlat_distinct_rows
+    CREATE UNIQUE INDEX fairway_marks_bcnlat_hydro_distinct_rows
         ON fairway_marks_bcnlat
         ((CAST((validity, last_found, geom,
                 datsta, datend, persta, perend, objnam, nobjnm, inform, ninfom,
                 scamin, picrep, txtdsc, sordat, sorind,
-                0, colour, colpat, condtn, bcnshp, catlam
-            ) AS fairway_marks_bcnlat)
+                colour, colpat, condtn, bcnshp, catlam, 0
+            ) AS fairway_marks_bcnlat_hydro)
+        ))
+
+    CREATE TABLE fairway_marks_bcnlat_ienc (
+        id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
+    ) INHERITS (fairway_marks_bcnlat)
+    -- Prevent identical entries using composite type comparison
+    -- (i.e. considering two NULL values in a field equal):
+    CREATE UNIQUE INDEX fairway_marks_bcnlat_ienc_distinct_rows
+        ON fairway_marks_bcnlat
+        ((CAST((validity, last_found, geom,
+                datsta, datend, persta, perend, objnam, nobjnm, inform, ninfom,
+                scamin, picrep, txtdsc, sordat, sorind,
+                colour, colpat, condtn, bcnshp, catlam, 0
+            ) AS fairway_marks_bcnlat_ienc)
         ))
 
     CREATE TABLE fairway_marks_bcnlat_dirimps (
-        fm_bcnlat_id int REFERENCES fairway_marks_bcnlat,
+        fm_bcnlat_id int REFERENCES fairway_marks_bcnlat_ienc,
         dirimp smallint REFERENCES dirimps,
         PRIMARY KEY (fm_bcnlat_id, dirimp)
     )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/schema/updates/1406/01.distinguish_bcnlat_hydro_ienc.sql	Thu Feb 27 21:05:09 2020 +0100
@@ -0,0 +1,58 @@
+CREATE TABLE waterway.fairway_marks_bcnlat_new (
+    colour varchar,
+    colpat varchar,
+    condtn int,
+    bcnshp int,
+    catlam int
+) INHERITS (waterway.fairway_marks);
+
+CREATE TABLE waterway.fairway_marks_bcnlat_hydro (
+    id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
+) INHERITS (waterway.fairway_marks_bcnlat_new);
+CREATE UNIQUE INDEX fairway_marks_bcnlat_hydro_distinct_rows
+    ON waterway.fairway_marks_bcnlat
+    ((CAST((validity, last_found, geom,
+            datsta, datend, persta, perend, objnam, nobjnm, inform, ninfom,
+            scamin, picrep, txtdsc, sordat, sorind,
+            colour, colpat, condtn, bcnshp, catlam, 0
+        ) AS waterway.fairway_marks_bcnlat_hydro)
+    ));
+
+CREATE TABLE waterway.fairway_marks_bcnlat_ienc (
+    id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
+) INHERITS (waterway.fairway_marks_bcnlat_new);
+CREATE UNIQUE INDEX fairway_marks_bcnlat_ienc_distinct_rows
+    ON waterway.fairway_marks_bcnlat
+    ((CAST((validity, last_found, geom,
+            datsta, datend, persta, perend, objnam, nobjnm, inform, ninfom,
+            scamin, picrep, txtdsc, sordat, sorind,
+            colour, colpat, condtn, bcnshp, catlam, 0
+        ) AS waterway.fairway_marks_bcnlat_ienc)
+    ));
+
+-- Assume all features are IENC features, since there is currently no known
+-- data source for HYDRO features
+INSERT INTO waterway.fairway_marks_bcnlat_ienc
+    SELECT validity, last_found, geom,
+            datsta, datend, persta, perend, objnam, nobjnm, inform, ninfom,
+            scamin, picrep, txtdsc, sordat, sorind,
+            colour, colpat, condtn, bcnshp, catlam, id
+        FROM waterway.fairway_marks_bcnlat;
+SELECT setval(
+        pg_get_serial_sequence('waterway.fairway_marks_bcnlat_ienc', 'id'),
+        max(id))
+    FROM waterway.fairway_marks_bcnlat_ienc;
+
+-- Let foreign key constraint point to new table
+ALTER TABLE waterway.fairway_marks_bcnlat_dirimps
+    DROP CONSTRAINT fairway_marks_bcnlat_dirimps_fm_bcnlat_id_fkey,
+    ADD FOREIGN KEY (fm_bcnlat_id)
+        REFERENCES waterway.fairway_marks_bcnlat_ienc;
+
+-- Finally
+DROP TABLE waterway.fairway_marks_bcnlat;
+ALTER TABLE waterway.fairway_marks_bcnlat_new RENAME TO fairway_marks_bcnlat;
+
+GRANT SELECT on ALL tables in schema waterway TO waterway_user;
+GRANT INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA waterway
+    TO waterway_admin;
--- a/schema/version.sql	Wed Feb 26 19:28:21 2020 +0100
+++ b/schema/version.sql	Thu Feb 27 21:05:09 2020 +0100
@@ -1,1 +1,1 @@
-INSERT INTO gemma_schema_version(version) VALUES (1405);
+INSERT INTO gemma_schema_version(version) VALUES (1406);