# HG changeset patch # User Markus Kottlaender # Date 1549535227 -3600 # Node ID 82867a69e10e0ccd146acbb963c6d32ed639bab9 # Parent 5a4b0c85e7a8fd31647a2f2590b680ff9734cd96# Parent 55bedb39295a461025c1076bff96557106c093ca merged default in pdf-export diff -r 5a4b0c85e7a8 -r 82867a69e10e client/src/components/ImportStretches.vue --- a/client/src/components/ImportStretches.vue Thu Feb 07 10:38:17 2019 +0100 +++ b/client/src/components/ImportStretches.vue Thu Feb 07 11:27:07 2019 +0100 @@ -263,6 +263,7 @@ import { mapState, mapGetters } from "vuex"; import { displayError, displayInfo } from "@/lib/errors.js"; import { formatSurveyDate } from "@/lib/date.js"; +import { LAYERS } from "@/store/map.js"; export default { name: "importstretches", @@ -356,6 +357,7 @@ }); }, moveMapToStretch(index) { + this.$store.commit("map/setLayerVisible", LAYERS.STRETCHES); this.$store.commit("map/moveToExtent", { feature: this.stretches[index], zoom: 17, @@ -406,6 +408,7 @@ this.edit = true; }, togglePipette(t) { + this.$store.commit("map/setLayerVisible", LAYERS.DISTANCEMARKSAXIS); if (t === "start") { this.pipetteStart = !this.pipetteStart; this.pipetteEnd = false; diff -r 5a4b0c85e7a8 -r 82867a69e10e client/src/components/Maplayer.vue --- a/client/src/components/Maplayer.vue Thu Feb 07 10:38:17 2019 +0100 +++ b/client/src/components/Maplayer.vue Thu Feb 07 11:27:07 2019 +0100 @@ -36,6 +36,7 @@ import "ol/ol.css"; import { Map, View } from "ol"; import { WFS, GeoJSON } from "ol/format.js"; +import { equalTo } from "ol/format/filter.js"; import { Stroke, Style, Fill } from "ol/style.js"; /* for the sake of debugging */ @@ -163,6 +164,7 @@ controls: [], view: new View({ center: [this.extent.lon, this.extent.lat], + minZoom: 5, // restrict zooming out to ~size of Europe for width 1000px zoom: this.extent.zoom, projection: this.projection }) @@ -179,33 +181,37 @@ // TODO make display of layers more dynamic, e.g. from a list - // loading the full WFS layer, by not setting the loader function - // and without bboxStrategy - var featureRequest = new WFS().writeGetFeature({ - srsName: "EPSG:3857", - featureNS: "gemma", - featurePrefix: "gemma", - featureTypes: ["fairway_dimensions"], - outputFormat: "application/json" - }); + // load different fairway dimension layers (level of service) + ["1", "2", "3"].forEach(los => { + // loading the full WFS layer, by not setting the loader function + // and without bboxStrategy + var featureRequest = new WFS().writeGetFeature({ + srsName: "EPSG:3857", + featureNS: "gemma", + featurePrefix: "gemma", + featureTypes: ["fairway_dimensions"], + outputFormat: "application/json", + filter: equalTo("level_of_service", los) + }); - // NOTE: loading the full fairway_dimensions makes sure - // that all are available for the intersection with the profile - HTTP.post( - "/internal/wfs", - new XMLSerializer().serializeToString(featureRequest), - { - headers: { - "X-Gemma-Auth": localStorage.getItem("token"), - "Content-type": "text/xml; charset=UTF-8" + // NOTE: loading the full fairway_dimensions makes sure + // that all are available for the intersection with the profile + HTTP.post( + "/internal/wfs", + new XMLSerializer().serializeToString(featureRequest), + { + headers: { + "X-Gemma-Auth": localStorage.getItem("token"), + "Content-type": "text/xml; charset=UTF-8" + } } - } - ).then(response => { - this.getVSourceByName("Fairway Dimensions").addFeatures( - new GeoJSON().readFeatures(JSON.stringify(response.data)) - ); - // would scale to the extend of all resulting features - // this.openLayersMap.getView().fit(vectorSrc.getExtent()); + ).then(response => { + this.getVSourceByName("Fairway Dimensions LOS " + los).addFeatures( + new GeoJSON().readFeatures(JSON.stringify(response.data)) + ); + // would scale to the extend of all resulting features + // this.openLayersMap.getView().fit(vectorSrc.getExtent()); + }); }); // load following layers with bboxStrategy (using our request builder) diff -r 5a4b0c85e7a8 -r 82867a69e10e client/src/components/Pdftool.vue --- a/client/src/components/Pdftool.vue Thu Feb 07 10:38:17 2019 +0100 +++ b/client/src/components/Pdftool.vue Thu Feb 07 11:27:07 2019 +0100 @@ -144,7 +144,7 @@ }, computed: { ...mapState("application", ["showPdfTool", "logoForPDF", "pdfTemplates"]), - ...mapState("bottlenecks", ["selectedSurvey"]), + ...mapState("bottlenecks", ["selectedBottleneck", "selectedSurvey"]), ...mapState("map", ["openLayersMap", "isolinesLegendImgDataURL"]), ...mapGetters("map", ["getLayerByName"]), ...mapState("user", ["user"]) @@ -254,7 +254,8 @@ //self.addAboutBox(pdf, width, height); if (self.getLayerByName("Bottleneck isolines").isVisible) { - self.addLegend(pdf, width, height); + self.addBottleneckInfo(pdf, 13, width, height); + self.addLegend(pdf, 14, width, height); } if (template) { template.elements.forEach(t => { @@ -406,7 +407,7 @@ unit = "km"; unitConversionFactor = 1e6; } else if (maxLength >= 1e4) { - // >= 10 m) + // >= 10 m unit = "m"; unitConversionFactor = 1e3; } @@ -519,7 +520,7 @@ "Dislaimer: Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua."; this.addText(doc, 5, docHeight - 6, 8, "black", 115, str); }, - addLegend(doc, docWidth) { + addLegend(doc, fromTop, docWidth) { // transforming into an HTMLImageElement only to find out // the width x height of the legend image // FUTURE: find a better way to get the width and height @@ -527,8 +528,46 @@ legendImage.src = this.isolinesLegendImgDataURL; let aspectRatio = legendImage.width / legendImage.height; - this.addRoundedBox(doc, docWidth - 54, 0, 54, 50 / aspectRatio + 4); - doc.addImage(legendImage, docWidth - 52, 2, 50, 50 / aspectRatio); + this.addRoundedBox(doc, docWidth - 54, fromTop, 54, 50 / aspectRatio + 4); + doc.addImage( + legendImage, + docWidth - 52, + fromTop + 2, + 50, + 50 / aspectRatio + ); + }, + addBottleneckInfo(doc, height, docWidth) { + this.addRoundedBox(doc, docWidth - 54, 0, 54, height); + + console.log("Fontlist =", doc.getFontList()); + doc.setFont("times", "normal"); + + let name, w, str; + + doc.setFontStyle("italic"); + name = this.$gettext("Bottleneck") + ": "; + w = doc.getTextWidth(name); + this.addText(doc, docWidth - 51, 4, 8, "black", 46, name); + doc.setFontStyle("bold"); + str = this.selectedBottleneck; + this.addText(doc, docWidth - 51 + w, 4, 8, "black", 46, str); + + doc.setFontStyle("italic"); + name = this.$gettext("Survey date") + ": "; + w = doc.getTextWidth(name); + this.addText(doc, docWidth - 51, 7.5, 8, "black", 46, name); + doc.setFontStyle("normal"); + str = this.selectedSurvey.date_info; + this.addText(doc, docWidth - 51 + w, 7.5, 8, "black", 46, str); + + doc.setFontStyle("italic"); + name = this.$gettext("Ref gauge") + ": "; + w = doc.getTextWidth(name); + this.addText(doc, docWidth - 51, 11, 8, "black", 46, name); + doc.setFontStyle("normal"); + str = this.selectedSurvey.gauge_objname; + this.addText(doc, docWidth - 51 + w, 11, 8, "black", 46, str); } }, mounted() { diff -r 5a4b0c85e7a8 -r 82867a69e10e client/src/components/staging/StagingDetail.vue --- a/client/src/components/staging/StagingDetail.vue Thu Feb 07 10:38:17 2019 +0100 +++ b/client/src/components/staging/StagingDetail.vue Thu Feb 07 11:27:07 2019 +0100 @@ -162,6 +162,7 @@ import { or as orFilter, equalTo as equalToFilter } from "ol/format/filter.js"; import { displayError } from "@/lib/errors.js"; import { mapState } from "vuex"; +import { LAYERS } from "@/store/map.js"; export default { name: "stagingdetail", @@ -206,6 +207,7 @@ }, methods: { zoomToStretch(name) { + this.$store.commit("map/setLayerVisible", LAYERS.STRETCHES); this.$store .dispatch("imports/loadStretch", name) .then(response => { @@ -301,6 +303,7 @@ return item.status === STATES.APPROVED; }, moveToBottleneck(index) { + this.$store.commit("map/setLayerVisible", LAYERS.BOTTLENECKS); this.moveToExtent(this.bottlenecks[index]); }, moveToExtent(feature) { diff -r 5a4b0c85e7a8 -r 82867a69e10e client/src/store/fairway.js --- a/client/src/store/fairway.js Thu Feb 07 10:38:17 2019 +0100 +++ b/client/src/store/fairway.js Thu Feb 07 11:27:07 2019 +0100 @@ -201,7 +201,7 @@ .then(() => { rootState.map.cutTool.setActive(false); rootGetters["map/getVSourceByName"]( - "Fairway Dimensions" + "Fairway Dimensions LOS 3" ).forEachFeatureIntersectingExtent( // need to use EPSG:3857 which is the proj of vectorSource profileLine diff -r 5a4b0c85e7a8 -r 82867a69e10e client/src/store/map.js --- a/client/src/store/map.js Thu Feb 07 10:38:17 2019 +0100 +++ b/client/src/store/map.js Thu Feb 07 11:27:07 2019 +0100 @@ -33,6 +33,23 @@ import bbox from "@turf/bbox"; import app from "../main"; +const LAYERS = { + OPENSTREETMAP: "Open Streetmap", + INLANDECDIS: "Inland ECDIS chart Danube", + WATERWAYAREA: "Waterway Area", + STRETCHES: "Stretches", + FAIRWAYDIMENSIONSLOS1: "Fairway Dimensions LOS 1", + FAIRWAYDIMENSIONSLOS2: "Fairway Dimensions LOS 2", + FAIRWAYDIMENSIONSLOS3: "Fairway Dimensions LOS 3", + WATERWAYAXIS: "Waterway Axis", + BOTTLENECKS: "Bottlenecks", + BOTTLENECKISOLINE: "Bottleneck isolines", + DISTANCEMARKS: "Distance marks", + DISTANCEMARKSAXIS: "Distance marks, Axis", + DRAWTOOL: "Draw Tool", + CUTTOOL: "Cut Tool" +}; + // initial state const init = () => { return { @@ -51,7 +68,7 @@ isolinesLegendImgDataURL: "", layers: [ { - name: "Open Streetmap", + name: LAYERS.OPENSTREETMAP, data: new TileLayer({ source: new OSM() }), @@ -59,7 +76,7 @@ showInLegend: true }, { - name: "Inland ECDIS chart Danube", + name: LAYERS.INLANDECDIS, data: new TileLayer({ source: new TileWMS({ preload: 1, @@ -72,7 +89,7 @@ showInLegend: true }, { - name: "Waterway Area", + name: LAYERS.WATERWAYAREA, data: new VectorLayer({ source: new VectorSource({ strategy: bboxStrategy @@ -88,7 +105,7 @@ showInLegend: true }, { - name: "Stretches", + name: LAYERS.STRETCHES, data: new VectorLayer({ source: new VectorSource({ strategy: bboxStrategy @@ -107,18 +124,14 @@ showInLegend: true }, { - name: "Fairway Dimensions", + name: LAYERS.FAIRWAYDIMENSIONSLOS1, data: new VectorLayer({ source: new VectorSource(), - style: function(feature) { - let strokeColor = - feature.get("level_of_service").toString() === "3" - ? "rgba(0, 0, 255, 1.0)" - : "rgba(0, 100, 150, 1.0)"; + style: function() { return [ new Style({ stroke: new Stroke({ - color: strokeColor, + color: "rgba(76, 76, 255, 1.0)", width: 2 }) }), @@ -129,7 +142,7 @@ fill: new Fill({ color: "black" }), - text: "LOS: " + feature.get("level_of_service").toString() + text: "LOS: 1" //, zIndex: 10 }) }) @@ -140,7 +153,65 @@ showInLegend: true }, { - name: "Waterway Axis", + name: LAYERS.FAIRWAYDIMENSIONSLOS2, + data: new VectorLayer({ + source: new VectorSource(), + style: function() { + return [ + new Style({ + stroke: new Stroke({ + color: "rgba(38, 38, 127, 1.0)", + width: 2 + }) + }), + new Style({ + text: new Text({ + font: 'bold 12px "Open Sans", "sans-serif"', + placement: "line", + fill: new Fill({ + color: "black" + }), + text: "LOS: 2" + //, zIndex: 10 + }) + }) + ]; + } + }), + isVisible: true, + showInLegend: true + }, + { + name: LAYERS.FAIRWAYDIMENSIONSLOS3, + data: new VectorLayer({ + source: new VectorSource(), + style: function() { + return [ + new Style({ + stroke: new Stroke({ + color: "rgba(0, 0, 255, 1.0)", + width: 2 + }) + }), + new Style({ + text: new Text({ + font: 'bold 12px "Open Sans", "sans-serif"', + placement: "line", + fill: new Fill({ + color: "black" + }), + text: "LOS: 3" + //, zIndex: 10 + }) + }) + ]; + } + }), + isVisible: true, + showInLegend: true + }, + { + name: LAYERS.WATERWAYAXIS, data: new VectorLayer({ source: new VectorSource({ strategy: bboxStrategy @@ -157,7 +228,7 @@ showInLegend: true }, { - name: "Bottlenecks", + name: LAYERS.BOTTLENECKS, data: new VectorLayer({ source: new VectorSource({ strategy: bboxStrategy @@ -176,7 +247,7 @@ showInLegend: true }, { - name: "Bottleneck isolines", + name: LAYERS.BOTTLENECKISOLINE, data: new TileLayer({ source: new TileWMS({ preload: 0, @@ -204,7 +275,7 @@ showInLegend: true }, { - name: "Distance marks", + name: LAYERS.DISTANCEMARKS, forLegendStyle: { point: true, resolution: 8 }, data: new VectorLayer({ source: new VectorSource({ @@ -215,7 +286,7 @@ showInLegend: true }, { - name: "Distance marks, Axis", + name: LAYERS.DISTANCEMARKSAXIS, forLegendStyle: { point: true, resolution: 8 }, data: new VectorLayer({ source: new VectorSource({ @@ -252,7 +323,7 @@ showInLegend: true }, { - name: "Draw Tool", + name: LAYERS.DRAWTOOL, data: new VectorLayer({ source: new VectorSource({ wrapX: false }), style: function(feature) { @@ -300,7 +371,7 @@ showInLegend: false }, { - name: "Cut Tool", + name: LAYERS.CUTTOOL, data: new VectorLayer({ source: new VectorSource({ wrapX: false }), style: function(feature) { @@ -371,6 +442,21 @@ extent: (state, extent) => { state.extent = extent; }, + setLayerVisible: (state, name) => { + const layer = state.layers.findIndex(l => l.name === name); + state.layers[layer].isVisible = true; + state.layers[layer].data.setVisible(true); + }, + setLayerInvisible: (state, name) => { + const layer = state.layers.findIndex(l => l.name === name); + state.layers[layer].isVisible = false; + state.layers[layer].data.setVisible(false); + }, + toggleVisibilityByName: (state, name) => { + const layer = state.layers.findIndex(l => l.name === name); + state.layers[layer].isVisible = !state.layers[layer].isVisible; + state.layers[layer].data.setVisible(state.layers[layer].isVisible); + }, toggleVisibility: (state, layer) => { state.layers[layer].isVisible = !state.layers[layer].isVisible; state.layers[layer].data.setVisible(state.layers[layer].isVisible); @@ -591,3 +677,5 @@ } } }; + +export { LAYERS }; diff -r 5a4b0c85e7a8 -r 82867a69e10e pkg/controllers/surveys.go --- a/pkg/controllers/surveys.go Thu Feb 07 10:38:17 2019 +0100 +++ b/pkg/controllers/surveys.go Thu Feb 07 11:27:07 2019 +0100 @@ -4,13 +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 Wilde // * Sascha L. Teichmann +// * Bernhard Reiter package controllers @@ -26,10 +27,15 @@ listSurveysSQL = ` SELECT s.bottleneck_id, - s.date_info::text -FROM waterway.bottlenecks b JOIN waterway.sounding_results s -ON b.id = s.bottleneck_id -WHERE b.objnam=$1` + s.date_info::text, + bg.objname AS gauge_objname +FROM + ( SELECT * FROM waterway.bottlenecks AS b, waterway.gauges AS g + WHERE b.fk_g_fid = g.location + ) AS bg + JOIN waterway.sounding_results AS s +ON bg.id = s.bottleneck_id +WHERE bg.objnam=$1` ) func listSurveys( @@ -55,6 +61,7 @@ if err = rows.Scan( &survey.BottleneckID, &survey.DateInfo, + &survey.ReferenceGauge, ); err != nil { return } diff -r 5a4b0c85e7a8 -r 82867a69e10e pkg/imports/wp.go --- a/pkg/imports/wp.go Thu Feb 07 10:38:17 2019 +0100 +++ b/pkg/imports/wp.go Thu Feb 07 11:27:07 2019 +0100 @@ -98,14 +98,6 @@ )` insertWaterwayProfileSQL = ` -WITH point AS ( - SELECT - ST_Buffer(geom, $14::float) AS geom, - geom AS point - FROM waterway.distance_marks_virtual - WHERE location_code = - ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int) -) INSERT INTO waterway.waterway_profiles ( location, geom, @@ -120,9 +112,11 @@ ) VALUES ( ($1, $2, $3, $4, $5), ( SELECT wp_geoms.geom - FROM wp_geoms, point - WHERE wp_geoms.geom && point.geom - ORDER BY ST_Distance(point.point, wp_geoms.geom, true) + FROM wp_geoms, waterway.distance_marks_virtual AS dmv + WHERE dmv.location_code = + ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int) + AND ST_DWithin(dmv.geom, wp_geoms.geom, $14::float) + ORDER BY ST_Distance(dmv.geom, wp_geoms.geom, true) LIMIT 1 ), $6, @@ -236,6 +230,10 @@ return err } + if _, err := tx.ExecContext(ctx, createTempIndexSQL); err != nil { + return err + } + insertStmt, err := tx.PrepareContext(ctx, insertGeomTmpTableSQL) if err != nil { return err diff -r 5a4b0c85e7a8 -r 82867a69e10e pkg/models/surveys.go --- a/pkg/models/surveys.go Thu Feb 07 10:38:17 2019 +0100 +++ b/pkg/models/surveys.go Thu Feb 07 11:27:07 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 // @@ -15,7 +15,8 @@ type ( Survey struct { - BottleneckID string `json:"bottleneck_id"` - DateInfo string `json:"date_info"` + BottleneckID string `json:"bottleneck_id"` + DateInfo string `json:"date_info"` + ReferenceGauge string `json:"gauge_objname"` } ) diff -r 5a4b0c85e7a8 -r 82867a69e10e schema/gemma.sql --- a/schema/gemma.sql Thu Feb 07 10:38:17 2019 +0100 +++ b/schema/gemma.sql Thu Feb 07 11:27:07 2019 +0100 @@ -329,7 +329,7 @@ -- At least geoserver-2.13.2 does not serve type geography correctly -- and does not serve the location_code as isrs type CREATE VIEW distance_marks_geoserver AS - SELECT location_code::VARCHAR, + SELECT isrs_asText(location_code), geom::Geometry(POINT, 4326), related_enc, (location_code).hectometre @@ -400,7 +400,6 @@ location isrs NOT NULL, geom geography(linestring, 4326), -- TODO: NOT NIL validity tstzrange, - EXCLUDE USING GIST (isrs_asText(location) WITH =, validity WITH &&), lnwl double precision, mwl double precision, hnwl double precision, @@ -412,7 +411,10 @@ date_info timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, source_organization varchar NOT NULL, staging_done boolean NOT NULL DEFAULT false, - UNIQUE (location, validity, staging_done) + EXCLUDE USING GIST ( + isrs_asText(location) WITH =, + validity WITH &&, + CAST(staging_done AS int) WITH =) ) CREATE TRIGGER waterway_profiles_date_info BEFORE UPDATE ON waterway_profiles