Mercurial > gemma
changeset 2563:dc4fae4bdb8f
Expose axis snapping tolerance to users
author | Tom Gottfried <tom@intevation.de> |
---|---|
date | Fri, 08 Mar 2019 19:15:47 +0100 |
parents | ce39e9954e85 |
children | 27501719e79b |
files | client/src/components/ImportStretches.vue client/src/components/importschedule/Importscheduledetail.vue client/src/components/importschedule/importtypes/Bottleneck.vue client/src/store/importschedule.js pkg/common/attributes.go pkg/controllers/uploadedimports.go pkg/imports/bn.go pkg/imports/modelconvert.go pkg/imports/st.go pkg/imports/ubn.go pkg/models/imports.go schema/isrs_functions.sql schema/isrs_tests.sql |
diffstat | 13 files changed, 195 insertions(+), 49 deletions(-) [+] |
line wrap: on
line diff
--- a/client/src/components/ImportStretches.vue Fri Mar 08 18:57:58 2019 +0100 +++ b/client/src/components/ImportStretches.vue Fri Mar 08 19:15:47 2019 +0100 @@ -154,6 +154,27 @@ <div class="d-flex flex-row justify-content-between"> <div class="mt-2 mr-2 w-50 text-left"> <small class="text-muted"> + <translate>Tolerance for snapping of waterway axis</translate> + </small> + <input + class="form-control" + v-model.number="tolerance" + placeholder="" + type="number" + step="any" + aria-label="tolerance" + id="tolerance" + /> + <span class="text-left text-danger"> + <small v-if="toleranceError && !tolerance"> + <translate>Please enter a tolerance value</translate> + </small> + </span> + </div> + </div> + <div class="d-flex flex-row justify-content-between"> + <div class="mt-2 mr-2 w-50 text-left"> + <small class="text-muted"> <translate>Object name</translate> </small> <input @@ -245,12 +266,13 @@ * SPDX-License-Identifier: AGPL-3.0-or-later * License-Filename: LICENSES/AGPL-3.0.txt * - * Copyright (C) 2018 by via donau + * Copyright (C) 2018, 2019 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * Thomas Junk <thomas.junk@intevation.de> + * Tom Gottfried <tom.gottfried@intevation.de> */ import { mapState, mapGetters } from "vuex"; import { displayError, displayInfo } from "@/lib/errors.js"; @@ -266,6 +288,7 @@ funktion: "", startrhm: "", endrhm: "", + tolerance: "", objbn: "", nobjbn: "", countryCode: "", @@ -277,6 +300,7 @@ funktionError: false, startrhmError: false, endrhmError: false, + toleranceError: false, objbnError: false, nobjbnError: false, date_infoError: false, @@ -339,6 +363,7 @@ this.edit = true; this.startrhm = properties.lower; this.endrhm = properties.upper; + this.tolerance = properties.tolerance; this.idEditable = false; }, deleteStretch(stretch) { @@ -393,6 +418,7 @@ this.idEditable = true; this.funktion = ""; this.startrhm = ""; + this.tolerance = ""; this.endrhm = ""; this.objbn = ""; this.nobjbn = ""; @@ -405,6 +431,7 @@ this.funktionError = false; this.startrhmError = false; this.endrhmError = false; + this.toleranceError = false; this.objbnError = false; this.nobjbnError = false; this.date_infoError = false; @@ -430,6 +457,7 @@ "id", "funktion", "startrhm", + "tolerance", "endrhm", "objbn", "nobjbn", @@ -452,6 +480,7 @@ !this.id || !this.startrhm || !this.endrhm || + !this.tolerance || !this.source || !this.date_info || !this.objbn || @@ -462,6 +491,7 @@ name: this.id, from: this.startrhm, to: this.endrhm, + tolerance: this.tolerance, "source-organization": this.source, "date-info": this.date_info, objnam: this.objbn,
--- a/client/src/components/importschedule/Importscheduledetail.vue Fri Mar 08 18:57:58 2019 +0100 +++ b/client/src/components/importschedule/Importscheduledetail.vue Fri Mar 08 19:15:47 2019 +0100 @@ -96,7 +96,9 @@ <Bottleneck v-if="import_ == $options.IMPORTTYPES.BOTTLENECK && !directImport" @urlChanged="setUrl" + @toleranceChanged="setTolerance" :url="url" + :tolerance="tolerance" ></Bottleneck> <Distancemarksvirtual v-if="import_ == $options.IMPORTTYPES.DISTANCEMARKSVIRTUAL" @@ -422,12 +424,13 @@ * SPDX-License-Identifier: AGPL-3.0-or-later * License-Filename: LICENSES/AGPL-3.0.txt * - * Copyright (C) 2018 by via donau + * Copyright (C) 2018, 2019 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * Thomas Junk <thomas.junk@intevation.de> + * Tom Gottfried <tom.gottfried@intevation.de> */ import { IMPORTTYPES, @@ -570,6 +573,14 @@ return false; } }, + isToleranceRequired() { + switch (this.import_) { + case this.$options.IMPORTTYPES.BOTTLENECK: + return true; + default: + return false; + } + }, isValid() { if (!this.import_) return false; if (this.directImport && !this.uploadFile) return false; @@ -577,6 +588,7 @@ if (this.isURLRequired && !this.url) return false; if (this.isSortbyRequired && !this.sortBy) return false; if (this.isFeatureTypeRequired && !this.featureType) return false; + if (this.isToleranceRequired && !this.tolerance) return false; if (this.isCredentialsRequired && (!this.username || !this.password)) return false; if (this.import_ == this.$options.IMPORTTYPES.FAIRWAYDIMENSION) { @@ -609,6 +621,9 @@ setSortBy(value) { this.sortBy = value; }, + setTolerance(value) { + this.tolerance = value; + }, setUsername(value) { this.username = value; }, @@ -673,6 +688,7 @@ this.cronString = this.currentSchedule.cronString; this.featureType = this.currentSchedule.featureType; this.sortBy = this.currentSchedule.sortBy; + this.tolerance = this.currentSchedule.tolerance; this.username = this.currentSchedule.username; this.password = this.currentSchedule.password; this.LOS = this.currentSchedule.LOS; @@ -765,6 +781,10 @@ if (!this.sortBy) return; data["sort-by"] = this.sortBy; } + if (this.isToleranceRequired) { + if (!this.tolerance) return; + data["tolerance"] = parseFloat(this.tolerance); + } if (this.isCredentialsRequired) { if (!this.username || !this.password) return; data["user"] = this.username; @@ -836,6 +856,10 @@ if (!this.featureType) return; config["feature-type"] = this.featureType; } + if (this.isToleranceRequired) { + if (!this.tolerance) return; + config["tolerance"] = parseFloat(this.tolerance); + } if (this.isCredentialsRequired) { if (!this.username || !this.password) return; config = {
--- a/client/src/components/importschedule/importtypes/Bottleneck.vue Fri Mar 08 18:57:58 2019 +0100 +++ b/client/src/components/importschedule/importtypes/Bottleneck.vue Fri Mar 08 19:15:47 2019 +0100 @@ -45,6 +45,30 @@ ><translate class="text-danger">Please enter a URL</translate></small > </div> + <div class="d-flex flex-row"> + <div class="flex-column mt-3 mr-3 w-50"> + <div class="flex-row text-left"> + <small class="text-muted"> + <translate>Tolerance for snapping of waterway axis</translate> + </small> + </div> + <div class="w-100"> + <input + @input="toleranceChanged" + class="tolerance form-control" + type="number" + :value="tolerance" + /> + </div> + <div v-if="!tolerance" class="d-flex flex-row"> + <small + ><translate class="text-danger" + >Please enter a tolerance value</translate + ></small + > + </div> + </div> + </div> </div> </template> @@ -55,19 +79,23 @@ * SPDX-License-Identifier: AGPL-3.0-or-later * License-Filename: LICENSES/AGPL-3.0.txt * - * Copyright (C) 2018 by via donau + * Copyright (C) 2018, 2019 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * Thomas Junk <thomas.junk@intevation.de> + * Tom Gottfried <tom.gottfried@intevation.de> */ export default { name: "bottleneckimport", - props: ["url"], + props: ["url", "tolerance"], methods: { urlChanged(e) { this.$emit("urlChanged", e.target.value); + }, + toleranceChanged(e) { + this.$emit("toleranceChanged", e.target.value); } } };
--- a/client/src/store/importschedule.js Fri Mar 08 18:57:58 2019 +0100 +++ b/client/src/store/importschedule.js Fri Mar 08 19:15:47 2019 +0100 @@ -77,6 +77,7 @@ triggerActive: true, featureType: null, sortBy: "hydro_scamin", + tolerance: 5, username: "", password: "", LOS: 3, @@ -181,6 +182,7 @@ let maxWidth = config["max-width"]; let sourceOrganization = config["source-organization"]; const featureType = config["feature-type"]; + const tolerance = config["tolerance"]; insecure = insecure == "true"; if (insecure) { Vue.set(state.currentSchedule, "insecure", insecure); @@ -191,6 +193,9 @@ if (sortBy) { Vue.set(state.currentSchedule, "sortBy", sortBy); } + if (tolerance) { + Vue.set(state.currentSchedule, "tolerance", tolerance); + } if (user) { Vue.set(state.currentSchedule, "username", user); }
--- a/pkg/common/attributes.go Fri Mar 08 18:57:58 2019 +0100 +++ b/pkg/common/attributes.go Fri Mar 08 19:15:47 2019 +0100 @@ -4,12 +4,13 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // License-Filename: LICENSES/AGPL-3.0.txt // -// Copyright (C) 2018 by via donau +// Copyright (C) 2018, 2019 by via donau // – Österreichische Wasserstraßen-Gesellschaft mbH // Software engineering by Intevation GmbH // // Author(s): // * Sascha L. Teichmann <sascha.teichmann@intevation.de> +// * Tom Gottfried <tom.gottfried@intevation.de> package common @@ -158,6 +159,24 @@ return ca.Set(key, v) } +func (ca Attributes) Float(key string) (float64, bool) { + s, found := ca.Get(key) + if !found { + return 0, false + } + f, err := strconv.ParseFloat(s, 64) + if err != nil { + log.Printf("error: %v\n", err) + return 0, false + } + return f, true +} + +func (ca Attributes) SetFloat(key string, value float64) bool { + v := strconv.FormatFloat(value, 'e', -1, 64) + return ca.Set(key, v) +} + func (ca Attributes) Duration(key string) (time.Duration, bool) { s, found := ca.Get(key) if !found {
--- a/pkg/controllers/uploadedimports.go Fri Mar 08 18:57:58 2019 +0100 +++ b/pkg/controllers/uploadedimports.go Fri Mar 08 19:15:47 2019 +0100 @@ -92,7 +92,8 @@ imports.UBNJobKind, "data.xml", func(_ *http.Request, dir string) (imports.Job, error) { - return &imports.UploadedBottleneck{Dir: dir}, nil + // TODO expose tolerance to endpoint + return &imports.UploadedBottleneck{Dir: dir, Tolerance: 5}, nil }, ) }
--- a/pkg/imports/bn.go Fri Mar 08 18:57:58 2019 +0100 +++ b/pkg/imports/bn.go Fri Mar 08 19:15:47 2019 +0100 @@ -30,6 +30,8 @@ type Bottleneck struct { // URL is the URL of the SOAP service. URL string `json:"url"` + // Tolerance used for axis snapping + Tolerance float64 `json:"tolerance"` // Insecure indicates if HTTPS traffic // should validate certificates or not. Insecure bool `json:"insecure"` @@ -64,7 +66,7 @@ $4, isrsrange(isrs_fromText($5), isrs_fromText($6)), ISRSrange_area( - isrsrange(isrs_fromText($5), isrs_fromText($6)), + ISRSrange_axis(isrsrange(isrs_fromText($5), isrs_fromText($6)), $14), (SELECT ST_Collect(CAST(area AS geometry)) FROM waterway.waterway_area)), $7, @@ -162,7 +164,7 @@ return resp.Export_bn_by_isrsResult.BottleNeckType, nil } - return storeBottlenecks(ctx, fetch, importID, conn, feedback) + return storeBottlenecks(ctx, fetch, importID, conn, feedback, bn.Tolerance) } func storeBottlenecks( @@ -171,6 +173,7 @@ importID int64, conn *sql.Conn, feedback Feedback, + tolerance float64, ) (interface{}, error) { start := time.Now() @@ -202,7 +205,7 @@ for _, bn := range bns { if err := storeBottleneck( - ctx, importID, conn, feedback, bn, &nids, + ctx, importID, conn, feedback, bn, &nids, tolerance, hasStmt, insertStmt, trackStmt); err != nil { return nil, err } @@ -228,6 +231,7 @@ feedback Feedback, bn *ifbn.BottleNeckType, nids *[]string, + tolerance float64, hasStmt, insertStmt, trackStmt *sql.Stmt, ) error { @@ -278,6 +282,7 @@ limiting, bn.Date_Info, bn.Source, + tolerance, ).Scan(&nid) if err != nil { feedback.Warn("Failed to insert '%s' into database", bn.OBJNAM)
--- a/pkg/imports/modelconvert.go Fri Mar 08 18:57:58 2019 +0100 +++ b/pkg/imports/modelconvert.go Fri Mar 08 19:15:47 2019 +0100 @@ -39,8 +39,9 @@ BNJobKind: func(input interface{}) interface{} { bi := input.(*models.BottleneckImport) return &Bottleneck{ - URL: bi.URL, - Insecure: bi.Insecure, + URL: bi.URL, + Tolerance: bi.Tolerance, + Insecure: bi.Insecure, } }, @@ -127,6 +128,7 @@ Name: sti.Name, From: sti.From, To: sti.To, + Tolerance: sti.Tolerance, ObjNam: sti.ObjNam, NObjNam: sti.NObjNam, Source: sti.Source,
--- a/pkg/imports/st.go Fri Mar 08 18:57:58 2019 +0100 +++ b/pkg/imports/st.go Fri Mar 08 19:15:47 2019 +0100 @@ -26,6 +26,7 @@ Name string `json:"name"` From models.Isrs `json:"from"` To models.Isrs `json:"to"` + Tolerance float32 `json:"tolerance"` ObjNam string `json:"objnam"` NObjNam *string `json:"nobjnam"` Source string `json:"source-organization"` @@ -99,7 +100,7 @@ $11, (SELECT r FROM r), ISRSrange_area( - (SELECT r FROM r), + ISRSrange_axis((SELECT r FROM r), $16::double precision), (SELECT ST_Collect(CAST(area AS geometry)) FROM waterway.waterway_area)), $12, @@ -191,6 +192,7 @@ nobjnm, st.Date.Time, st.Source, + st.Tolerance, ).Scan(&id); err != nil { return nil, handleError(err) }
--- a/pkg/imports/ubn.go Fri Mar 08 18:57:58 2019 +0100 +++ b/pkg/imports/ubn.go Fri Mar 08 19:15:47 2019 +0100 @@ -26,6 +26,8 @@ type UploadedBottleneck struct { Dir string `json:"dir"` + // Tolerance used for axis snapping + Tolerance float64 `json:"tolerance"` } // UBNJobKind is the import queue type identifier. @@ -84,5 +86,6 @@ return dst.Export_bn_by_isrsResult.BottleNeckType, nil } - return storeBottlenecks(ctx, fetch, importID, conn, feedback) + return storeBottlenecks( + ctx, fetch, importID, conn, feedback, ubn.Tolerance) }
--- a/pkg/models/imports.go Fri Mar 08 18:57:58 2019 +0100 +++ b/pkg/models/imports.go Fri Mar 08 19:15:47 2019 +0100 @@ -4,12 +4,14 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // License-Filename: LICENSES/AGPL-3.0.txt // -// Copyright (C) 2018 by via donau +// Copyright (C) 2018, 2019 by via donau // – Österreichische Wasserstraßen-Gesellschaft mbH // Software engineering by Intevation GmbH // // Author(s): // * Sascha L. Teichmann <sascha.teichmann@intevation.de> +// * Tom Gottfried <tom.gottfried@intevation.de> + package models import ( @@ -27,6 +29,9 @@ BottleneckImport struct { ConfigurableURLImport + + // Tolerance used for axis snapping + Tolerance float64 `json:"tolerance"` } // GaugeMeasurementImport contains data used to define the endpoint @@ -95,6 +100,7 @@ Name string `json:"name"` From Isrs `json:"from"` To Isrs `json:"to"` + Tolerance float32 `json:"tolerance"` ObjNam string `json:"objnam"` NObjNam *string `json:"nobjnam"` Source string `json:"source-organization"` @@ -143,6 +149,26 @@ return nil } +func (bn *BottleneckImport) MarshalAttributes(attrs common.Attributes) error { + if err := bn.ConfigurableURLImport.MarshalAttributes(attrs); err != nil { + return err + } + attrs.SetFloat("tolerance", bn.Tolerance) + return nil +} + +func (bn *BottleneckImport) UnmarshalAttributes(attrs common.Attributes) error { + if err := bn.ConfigurableURLImport.UnmarshalAttributes(attrs); err != nil { + return err + } + tol, found := attrs.Float("tolerance") + if !found { + return errors.New("missing 'tolerance' attribute") + } + bn.Tolerance = tol + return nil +} + func (fdi *FairwayDimensionImport) MarshalAttributes(attrs common.Attributes) error { if err := fdi.WFSImport.MarshalAttributes(attrs); err != nil { return err
--- a/schema/isrs_functions.sql Fri Mar 08 18:57:58 2019 +0100 +++ b/schema/isrs_functions.sql Fri Mar 08 19:15:47 2019 +0100 @@ -47,6 +47,7 @@ -- Uses the table waterway.distance_marks_virtual to map ISRS location codes -- to their geo-location. -- Distance marks are assumed to be near the axis. +-- Returns the axis geometry transformed to the best matching UTM zone. CREATE OR REPLACE FUNCTION ISRSrange_axis( stretch isrsrange, tolerance float @@ -135,32 +136,28 @@ LANGUAGE plpgsql STABLE PARALLEL SAFE; --- Clip an area to a stretch given by a pair of ISRS location codes. --- Uses ISRSrange_axis() to retrieve the respective clipped axis used to find --- perpendicular direction at geo-locations of ISRS codes. +-- Clip an area to a stretch given by a geometry representing an axis (e.g. +-- the output of ISRSrange_axis()). +-- Clipping is done by cutting the area in perpendicular direction at +-- the ends of the given axis. -- The area passed as argument is assumed to intersect with the axis -- (use e.g. waterway area or fairway dimensions). -- If a multipolygon is passed, the union of the polygons intersecting with the --- relevant part of the axis is used for clipping. +-- axis is used for clipping. CREATE OR REPLACE FUNCTION ISRSrange_area( - stretch isrsrange, + axis geometry, area geometry ) RETURNS geometry AS $$ WITH - axis_substring AS ( - SELECT ISRSrange_axis(stretch, 5) AS line), - utm_zone AS ( - SELECT best_utm(stretch) AS z), area_subset AS ( -- In case area is a multipolygon, process the union of those -- polygons, which intersect with the axis. The union is to avoid -- problems with invalid/self-intersecting multipolygons SELECT ST_Union(a_dmp.geom) AS area - FROM axis_substring, utm_zone, LATERAL ( - SELECT ST_MakeValid(ST_Transform(geom, z)) AS geom - FROM ST_Dump(area)) AS a_dmp - WHERE ST_Intersects(a_dmp.geom, axis_substring.line) + FROM (SELECT ST_MakeValid(ST_Transform(geom, ST_SRID(axis))) + FROM ST_Dump(area)) AS a_dmp (geom) + WHERE ST_Intersects(a_dmp.geom, axis) ), rotated_ends AS ( SELECT ST_Collect(ST_Scale( @@ -168,11 +165,11 @@ (ST_X(p1) - ST_X(p2)) / 2, (ST_Y(p1) - ST_Y(p2)) / 2), ST_Point(d, d), p1)) AS blade - FROM axis_substring, area_subset, - LATERAL (SELECT i, ST_PointN(line, i) AS p1 + FROM (SELECT i, ST_PointN(axis, i) AS p1 FROM (VALUES (1), (-1)) AS idx (i)) AS ep, - ST_Rotate(ST_PointN(line, i*2), pi()/2, p1) AS ep2 (p2), + ST_Rotate(ST_PointN(axis, i*2), pi()/2, p1) AS ep2 (p2), ST_Makeline(p1, p2) AS e (e), + area_subset, LATERAL (SELECT (ST_MaxDistance(p1, area) / ST_Length(e)) * 2) AS d (d)), range_area AS ( @@ -183,8 +180,7 @@ -- From the polygons returned by the last CTE, select only those -- around the clipped axis SELECT ST_Multi(ST_Transform(ST_Union(range_area.geom), ST_SRID(area))) - FROM axis_substring, range_area - WHERE ST_Intersects(ST_Buffer(range_area.geom, -0.0001), - axis_substring.line) + FROM range_area + WHERE ST_Intersects(ST_Buffer(range_area.geom, -0.0001), axis) $$ LANGUAGE sql;
--- a/schema/isrs_tests.sql Fri Mar 08 18:57:58 2019 +0100 +++ b/schema/isrs_tests.sql Fri Mar 08 19:15:47 2019 +0100 @@ -4,7 +4,7 @@ -- SPDX-License-Identifier: AGPL-3.0-or-later -- License-Filename: LICENSES/AGPL-3.0.txt --- Copyright (C) 2018 by via donau +-- Copyright (C) 2018, 2019 by via donau -- – Österreichische Wasserstraßen-Gesellschaft mbH -- Software engineering by Intevation GmbH @@ -52,9 +52,10 @@ 'ISRSrange_axis fails if no contiguous axis can be constructed'); SELECT ok( - ISRSrange_area(isrsrange( - ('AT', 'XXX', '00001', '00000', 0)::isrs, - ('AT', 'XXX', '00001', '00000', 1)::isrs), + ISRSrange_area(ISRSrange_axis(isrsrange( + ('AT', 'XXX', '00001', '00000', 0)::isrs, + ('AT', 'XXX', '00001', '00000', 1)::isrs), + 5), ST_SetSRID('POLYGON((0 1, 0 2, 1 2, 1 1, 0 1))'::geometry, 4326) ) IS NULL, 'ISRSrange_area returns NULL, if given area does not intersect with axis'); @@ -62,9 +63,10 @@ SELECT results_eq($$ SELECT every(ST_DWithin( ST_Boundary(ISRSrange_area( - isrsrange( - ('AT', 'XXX', '00001', '00000', 0)::isrs, - ('AT', 'XXX', '00001', '00000', 1)::isrs), + ISRSrange_axis(isrsrange( + ('AT', 'XXX', '00001', '00000', 0)::isrs, + ('AT', 'XXX', '00001', '00000', 1)::isrs), + 5), (SELECT ST_Collect(CAST(area AS geometry)) FROM waterway.waterway_area))), geom, @@ -83,9 +85,10 @@ SELECT ok( 2 = ST_NumGeometries( ISRSrange_area( - isrsrange( - ('AT', 'XXX', '00001', '00000', 0)::isrs, - ('AT', 'XXX', '00001', '00000', 1)::isrs), + ISRSrange_axis(isrsrange( + ('AT', 'XXX', '00001', '00000', 0)::isrs, + ('AT', 'XXX', '00001', '00000', 1)::isrs), + 5), ST_SetSRID(ST_Collect( ST_Translate(:'test_area', (ST_XMax(:'test_area'::geometry) @@ -101,9 +104,10 @@ SELECT ok( 1 = ST_NumGeometries( ISRSrange_area( - isrsrange( - ('AT', 'XXX', '00001', '00000', 0)::isrs, - ('AT', 'XXX', '00001', '00000', 1)::isrs), + ISRSrange_axis(isrsrange( + ('AT', 'XXX', '00001', '00000', 0)::isrs, + ('AT', 'XXX', '00001', '00000', 1)::isrs), + 5), ST_SetSRID(ST_Collect( ST_Translate(:'test_area', (ST_XMax(:'test_area'::geometry) @@ -119,9 +123,10 @@ SELECT results_eq($$ SELECT every(ST_DWithin( ST_Boundary(ISRSrange_area( - isrsrange( - ('AT', 'XXX', '00001', '00000', 0)::isrs, - ('AT', 'XXX', '00001', '00000', 2)::isrs), + ISRSrange_axis(isrsrange( + ('AT', 'XXX', '00001', '00000', 0)::isrs, + ('AT', 'XXX', '00001', '00000', 2)::isrs), + 5), (SELECT ST_Collect(CAST(area AS geometry)) FROM waterway.waterway_area))), geom,