# HG changeset patch # User Markus Kottlaender # Date 1551684725 -3600 # Node ID 7247eb03e7c08057e8d1cf23acf474fb5d599666 # Parent ae1987c5beb39b64fbc6a1e59e44c03d3ccfaff7# Parent 204b0baac93c19533e2e1cdfa3c52fcae9a8cbe7 merged default into critical-bottlenecks branch diff -r ae1987c5beb3 -r 7247eb03e7c0 client/src/components/Maplayer.vue --- a/client/src/components/Maplayer.vue Fri Mar 01 14:55:58 2019 +0100 +++ b/client/src/components/Maplayer.vue Mon Mar 04 08:32:05 2019 +0100 @@ -337,6 +337,21 @@ ); layer.data.setVisible(layer.isVisible); + layer = this.getLayerByName(LAYERS.GAUGES); + layer.data.getSource().setLoader( + this.buildVectorLoader( + { + featureNS: "gemma", + featurePrefix: "gemma", + featureTypes: ["gauges_geoserver"], + geometryName: "geom" + }, + "/internal/wfs", + layer.data.getSource() + ) + ); + layer.data.setVisible(layer.isVisible); + layer = this.getLayerByName(LAYERS.STRETCHES); layer.data.getSource().setLoader( this.buildVectorLoader( diff -r ae1987c5beb3 -r 7247eb03e7c0 client/src/components/Search.vue --- a/client/src/components/Search.vue Fri Mar 01 14:55:58 2019 +0100 +++ b/client/src/components/Search.vue Mon Mar 04 08:32:05 2019 +0100 @@ -58,6 +58,12 @@ class="mr-1" fixed-width /> + {{ entry.name }} @@ -269,6 +275,7 @@ if (resultEntry.type === "bottleneck") zoom = 17; if (resultEntry.type === "rhm") zoom = 15; if (resultEntry.type === "city") zoom = 13; + if (resultEntry.type === "gauge") zoom = 15; this.$store.commit("map/moveMap", { coordinates: resultEntry.geom.coordinates, zoom, diff -r ae1987c5beb3 -r 7247eb03e7c0 client/src/components/Sidebar.vue --- a/client/src/components/Sidebar.vue Fri Mar 01 14:55:58 2019 +0100 +++ b/client/src/components/Sidebar.vue Mon Mar 04 08:32:05 2019 +0100 @@ -199,10 +199,8 @@ this.contextBoxContent === item && this.routeName == "mainview" ); - } - }, - mounted() { - setTimeout(() => { + }, + updateIndicators() { this.$store.dispatch("imports/getStaging").catch(error => { const { status, data } = error.response; displayError({ @@ -210,7 +208,11 @@ message: `${status}: ${data.message || data}` }); }); - }, 15000); + } + }, + mounted() { + this.updateIndicators(); + setTimeout(this.updateIndicators, 15000); } }; diff -r ae1987c5beb3 -r 7247eb03e7c0 client/src/components/identify/formatter.js --- a/client/src/components/identify/formatter.js Fri Mar 01 14:55:58 2019 +0100 +++ b/client/src/components/identify/formatter.js Mon Mar 04 08:32:05 2019 +0100 @@ -37,6 +37,9 @@ }, stretches_geoserver: { label: "Stretch" + }, + gauges_geoserver: { + label: "Gauge" } }; diff -r ae1987c5beb3 -r 7247eb03e7c0 client/src/store/map.js --- a/client/src/store/map.js Fri Mar 01 14:55:58 2019 +0100 +++ b/client/src/store/map.js Mon Mar 04 08:32:05 2019 +0100 @@ -20,7 +20,15 @@ import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js"; import OSM from "ol/source/OSM"; import Draw from "ol/interaction/Draw.js"; -import { Icon, Stroke, Style, Fill, Text, Circle } from "ol/style.js"; +import { + Icon, + Stroke, + Style, + Fill, + Text, + Circle, + RegularShape +} from "ol/style.js"; import VectorSource from "ol/source/Vector.js"; import Point from "ol/geom/Point.js"; import { bbox as bboxStrategy } from "ol/loadingstrategy"; @@ -48,6 +56,7 @@ BOTTLENECKISOLINE: "Bottleneck isolines", DISTANCEMARKS: "Distance marks", DISTANCEMARKSAXIS: "Distance marks, Axis", + GAUGES: "Gauges", DRAWTOOL: "Draw Tool", CUTTOOL: "Cut Tool" }; @@ -401,6 +410,39 @@ showInLegend: true }, { + name: LAYERS.GAUGES, + forLegendStyle: { point: true, resolution: 8 }, + data: new VectorLayer({ + source: new VectorSource({ + strategy: bboxStrategy + }), + style: function(feature) { + return new Style({ + image: new RegularShape({ + radius: 6, + fill: new Fill({ color: "rgba(255, 255, 0, 0.1)" }), + stroke: new Stroke({ color: "red", width: 1 }), + points: 3, + rotation: 0, + angle: 0 + }), + text: new Text({ + font: '10px "Open Sans", "sans-serif"', + offsetY: 10, + fill: new Fill({ + color: "black" + }), + text: feature.get("objname") + }) + }); + }, + maxResolution: 100, + minResolution: 0 + }), + isVisible: true, + showInLegend: true + }, + { name: LAYERS.DRAWTOOL, data: new VectorLayer({ source: new VectorSource({ wrapX: false }), diff -r ae1987c5beb3 -r 7247eb03e7c0 schema/demo-data/published_services.sql --- a/schema/demo-data/published_services.sql Fri Mar 01 14:55:58 2019 +0100 +++ b/schema/demo-data/published_services.sql Mon Mar 04 08:32:05 2019 +0100 @@ -14,6 +14,7 @@ INSERT INTO sys_admin.published_services (name) VALUES ('waterway.stretches_geoserver'), ('waterway.fairway_dimensions'), + ('waterway.gauges_geoserver'), ('waterway.distance_marks_ashore_geoserver'), ('waterway.distance_marks_geoserver'), ('waterway.sounding_results_contour_lines_geoserver'), diff -r ae1987c5beb3 -r 7247eb03e7c0 schema/gemma.sql --- a/schema/gemma.sql Fri Mar 01 14:55:58 2019 +0100 +++ b/schema/gemma.sql Mon Mar 04 08:32:05 2019 +0100 @@ -252,6 +252,28 @@ value int NOT NULL ) + CREATE VIEW gauges_geoserver AS + SELECT + g.location, + isrs_asText(g.location) AS isrs_code, + g.objname, + g.geom, + g.applicability_from_km, + g.applicability_to_km, + g.validity, + g.zero_point, + g.geodref, + g.date_info, + g.source_organization, + json_strip_nulls(json_object_agg(coalesce(r.depth_reference,'empty'), + r.value)) + AS reference_water_levels + FROM gauges g LEFT JOIN LATERAL ( + SELECT gauge_id, depth_reference, value + FROM gauges_reference_water_levels + ) r ON r.gauge_id = g.location + GROUP BY g.location + CREATE TABLE gauge_measurements ( id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, fk_gauge_id isrs NOT NULL REFERENCES gauges, @@ -630,6 +652,7 @@ -- Configure primary keys for geoserver views INSERT INTO waterway.gt_pk_metadata VALUES + ('waterway', 'gauges_geoserver', 'location'), ('waterway', 'distance_marks_geoserver', 'location_code'), ('waterway', 'distance_marks_ashore_geoserver', 'id'), ('waterway', 'bottlenecks_geoserver', 'id'), diff -r ae1987c5beb3 -r 7247eb03e7c0 schema/isrs_functions.sql --- a/schema/isrs_functions.sql Fri Mar 01 14:55:58 2019 +0100 +++ b/schema/isrs_functions.sql Mon Mar 04 08:32:05 2019 +0100 @@ -12,19 +12,20 @@ -- * Tom Gottfried -- * Sascha Wilde --- Clip an area to a stretch given by a pair of ISRS location codes. +CREATE OR REPLACE FUNCTION best_utm(stretch isrsrange) RETURNS integer +AS $$ + SELECT best_utm(ST_Collect(geom::geometry)) + FROM waterway.distance_marks_virtual + WHERE location_code IN (lower(stretch), upper(stretch)) + $$ + LANGUAGE sql + STABLE PARALLEL SAFE; + +-- Clip waterway axis to a stretch given by a pair of ISRS location codes. -- Uses the table waterway.distance_marks_virtual to map ISRS location codes --- to their geo-location and the table waterway.waterway_axis to retrieve --- perpendicular direction at these geo-locations. --- Distance marks are assumed to be near the axis and 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. -CREATE OR REPLACE FUNCTION ISRSrange_area( - stretch isrsrange, - area geometry -) RETURNS geometry +-- to their geo-location. +-- Distance marks are assumed to be near the axis. +CREATE OR REPLACE FUNCTION ISRSrange_axis(stretch isrsrange) RETURNS geometry AS $$ WITH RECURSIVE -- Get coordinates of location codes @@ -34,8 +35,7 @@ OR location_code = upper(stretch)), utm_zone AS ( -- Find best matchting UTM zone - SELECT best_utm(ST_Collect(geom::geometry)) AS z - FROM points_geog), + SELECT best_utm(stretch) AS z), axis AS ( SELECT id, ST_Transform(wtwaxs::geometry, z) AS wtwaxs FROM waterway.waterway_axis, utm_zone), @@ -66,11 +66,8 @@ SELECT refgeom UNION -- Fill eventual gap - SELECT ST_MakeLine( - ST_ClosestPoint( - ST_Boundary(refgeom), ST_Boundary(geom)), - ST_ClosestPoint( - ST_Boundary(geom), ST_Boundary(refgeom))) + SELECT ST_ShortestLine( + ST_Boundary(refgeom), ST_Boundary(geom)) UNION -- Linestring to be added SELECT geom))) @@ -87,23 +84,43 @@ SELECT wtwaxs AS line FROM axis_snapped WHERE array_length(ids, 1) = ( - SELECT max(array_length(ids, 1)) FROM axis_snapped)), + SELECT max(array_length(ids, 1)) FROM axis_snapped)) + -- Use linear referencing to clip axis between distance marks. + -- Simplification is used to work-around the problem, that + -- ST_LineSubstring might generate very small line segments at an + -- end of the resulting linestring, that significantly differ from + -- the direction of the input linestring due to finite precision + -- of the calculation. The generated small segment of the + -- resulting line leads to unexpected results of the buffer with + -- endcap=flat in the CTE below. + SELECT ST_SimplifyPreserveTopology(ST_LineSubstring( + axis_segment.line, min(fractions.f), max(fractions.f)), + 0.0001) AS line + FROM axis_segment, LATERAL ( + SELECT ST_LineLocatePoint(axis_segment.line, points.geom) AS f + FROM points) AS fractions + GROUP BY axis_segment.line + $$ + LANGUAGE sql + 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. +-- 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. +CREATE OR REPLACE FUNCTION ISRSrange_area( + stretch isrsrange, + area geometry +) RETURNS geometry +AS $$ + WITH axis_substring AS ( - -- Use linear referencing to clip axis between distance marks. - -- Simplification is used to work-around the problem, that - -- ST_LineSubstring might generate very small line segments at an - -- end of the resulting linestring, that significantly differ from - -- the direction of the input linestring due to finite precision - -- of the calculation. The generated small segment of the - -- resulting line leads to unexpected results of the buffer with - -- endcap=flat in the CTE below. - SELECT ST_SimplifyPreserveTopology(ST_LineSubstring( - axis_segment.line, min(fractions.f), max(fractions.f)), - 0.0001) AS line - FROM axis_segment, LATERAL ( - SELECT ST_LineLocatePoint(axis_segment.line, points.geom) AS f - FROM points) AS fractions - GROUP BY axis_segment.line), + SELECT ISRSrange_axis(stretch) 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 diff -r ae1987c5beb3 -r 7247eb03e7c0 schema/isrs_tests.sql --- a/schema/isrs_tests.sql Fri Mar 01 14:55:58 2019 +0100 +++ b/schema/isrs_tests.sql Mon Mar 04 08:32:05 2019 +0100 @@ -42,26 +42,24 @@ ) IS NULL, 'ISRSrange_area returns NULL, if given area does not intersect with axis'); -SELECT ok( - ST_DWithin( - (SELECT geom FROM waterway.distance_marks_virtual - WHERE location_code = ('AT', 'XXX', '00001', '00000', 0)::isrs), - ST_Boundary(ISRSrange_area(isrsrange( - ('AT', 'XXX', '00001', '00000', 0)::isrs, - ('AT', 'XXX', '00001', '00000', 1)::isrs), - (SELECT ST_Collect(CAST(area AS geometry)) - FROM waterway.waterway_area))), - 1) - AND - ST_DWithin( - (SELECT geom FROM waterway.distance_marks_virtual - WHERE location_code = ('AT', 'XXX', '00001', '00000', 1)::isrs), - ST_Boundary(ISRSrange_area(isrsrange( - ('AT', 'XXX', '00001', '00000', 0)::isrs, - ('AT', 'XXX', '00001', '00000', 1)::isrs), - (SELECT ST_Collect(CAST(area AS geometry)) - FROM waterway.waterway_area))), - 1), +SELECT results_eq($$ + SELECT every(ST_DWithin( + ST_Boundary(ISRSrange_area( + isrsrange( + ('AT', 'XXX', '00001', '00000', 0)::isrs, + ('AT', 'XXX', '00001', '00000', 1)::isrs), + (SELECT ST_Collect(CAST(area AS geometry)) + FROM waterway.waterway_area))), + geom, + 1)) + FROM waterway.distance_marks_virtual + WHERE location_code IN( + ('AT', 'XXX', '00001', '00000', 0)::isrs, + ('AT', 'XXX', '00001', '00000', 1)::isrs) + $$, + $$ + SELECT true + $$, 'Resulting polygon almost ST_Touches points corresponding to stretch'); \set test_area 'POLYGON((-1 1, 2 1, 2 -1, -1 -1, -1 1))' @@ -101,11 +99,22 @@ 4326))), 'Self-intersecting multipolygon leads to one polygon in result'); -SELECT ok( - ISRSrange_area( - isrsrange( +SELECT results_eq($$ + SELECT every(ST_DWithin( + ST_Boundary(ISRSrange_area( + isrsrange( + ('AT', 'XXX', '00001', '00000', 0)::isrs, + ('AT', 'XXX', '00001', '00000', 2)::isrs), + (SELECT ST_Collect(CAST(area AS geometry)) + FROM waterway.waterway_area))), + geom, + 1)) + FROM waterway.distance_marks_virtual + WHERE location_code IN( ('AT', 'XXX', '00001', '00000', 0)::isrs, - ('AT', 'XXX', '00001', '00000', 2)::isrs), - (SELECT ST_Collect(CAST(area AS geometry)) - FROM waterway.waterway_area)) IS NOT NULL, + ('AT', 'XXX', '00001', '00000', 2)::isrs) + $$, + $$ + SELECT true + $$, 'Area generated from non-matching distance mark and non-contiguous axis'); diff -r ae1987c5beb3 -r 7247eb03e7c0 schema/search_functions.sql --- a/schema/search_functions.sql Fri Mar 01 14:55:58 2019 +0100 +++ b/schema/search_functions.sql Mon Mar 04 08:32:05 2019 +0100 @@ -51,10 +51,30 @@ END; $$; +CREATE OR REPLACE FUNCTION search_gauges(search_string text) RETURNS jsonb + LANGUAGE plpgsql + AS $$ +DECLARE + _result jsonb; +BEGIN + SELECT COALESCE(json_agg(r),'[]') + INTO _result + FROM (SELECT objname AS name, + ST_AsGeoJSON(geom)::json AS geom, + 'gauge' AS type + FROM waterway.gauges + WHERE objname ILIKE '%' || search_string || '%' + ORDER BY name) r; + RETURN _result; +END; +$$; + CREATE OR REPLACE FUNCTION search_most(search_string text) RETURNS jsonb LANGUAGE plpgsql AS $$ BEGIN - RETURN search_bottlenecks(search_string) || search_cities(search_string); + RETURN search_bottlenecks(search_string) + || search_gauges(search_string) + || search_cities(search_string); END; $$;