comparison client/src/components/gauge/HydrologicalConditions.vue @ 3197:9d38df37c1f8

client: implemnt pdf-template for hydrologicalconditions diagram
author Fadi Abbud <fadi.abbud@intevation.de>
date Wed, 08 May 2019 15:08:27 +0200
parents cdfb0093b7b1
children 5b8916b78cea
comparison
equal deleted inserted replaced
3196:0d76a0476a5b 3197:9d38df37c1f8
23 </div> 23 </div>
24 <div class="legend"> 24 <div class="legend">
25 <span style="background-color: lightsteelblue"></span> 25 <span style="background-color: lightsteelblue"></span>
26 Long-term Amplitude 26 Long-term Amplitude
27 </div> 27 </div>
28 <select
29 @change="applyChange"
30 v-model="form.template"
31 class="form-control d-block custom-select-sm w-100"
32 >
33 <option
34 v-for="template in templates"
35 :value="template"
36 :key="template.name"
37 >
38 {{ template.name }}
39 </option>
40 </select>
28 <div> 41 <div>
29 <button 42 <button
30 @click="downloadPDF" 43 @click="downloadPDF"
31 type="button" 44 type="button"
32 class="btn btn-sm btn-info d-block w-100 mt-2" 45 class="btn btn-sm btn-info d-block w-100 mt-2"
67 import * as d3 from "d3"; 80 import * as d3 from "d3";
68 import debounce from "debounce"; 81 import debounce from "debounce";
69 import { startOfYear, endOfYear } from "date-fns"; 82 import { startOfYear, endOfYear } from "date-fns";
70 import jsPDF from "jspdf"; 83 import jsPDF from "jspdf";
71 import canvg from "canvg"; 84 import canvg from "canvg";
85 import { pdfgen } from "@/lib/mixins";
86 import { HTTP } from "@/lib/http";
87 import { displayError } from "@/lib/errors";
72 88
73 export default { 89 export default {
90 mixins: [pdfgen],
74 components: { 91 components: {
75 DiagramLegend: () => import("@/components/DiagramLegend") 92 DiagramLegend: () => import("@/components/DiagramLegend")
76 }, 93 },
77 data() { 94 data() {
78 return { 95 return {
81 diagram: null, 98 diagram: null,
82 navigation: null, 99 navigation: null,
83 dimensions: null, 100 dimensions: null,
84 extent: null, 101 extent: null,
85 scale: null, 102 scale: null,
86 axes: null 103 axes: null,
104 templateData: null,
105 form: {
106 template: null,
107 form: null
108 },
109 templates: [],
110 defaultTemplate: {
111 name: "Default",
112 properties: {
113 paperSize: "a4",
114 resolution: 80
115 },
116 elements: [
117 {
118 type: "diagram",
119 position: "topleft",
120 offset: { x: 15, y: 50 },
121 width: 290,
122 height: 100
123 },
124 {
125 type: "diagramlegend",
126 position: "topleft",
127 offset: { x: 30, y: 150 },
128 colot: "black"
129 },
130 {
131 type: "diagramtitle",
132 position: "topleft",
133 offset: { x: 50, y: 26 },
134 fontsize: 22,
135 color: "steelblue"
136 }
137 ]
138 },
139 pdf: {
140 doc: null,
141 width: 420,
142 height: 297
143 }
87 }; 144 };
88 }, 145 },
89 computed: { 146 computed: {
90 ...mapState("application", ["paneSetup"]), 147 ...mapState("application", ["paneSetup"]),
91 ...mapState("gauges", [ 148 ...mapState("gauges", [
120 ? "GAUGE_WATERLEVEL" 177 ? "GAUGE_WATERLEVEL"
121 : "DEFAULT" 178 : "DEFAULT"
122 ); 179 );
123 }, 180 },
124 downloadPDF() { 181 downloadPDF() {
182 if (this.templateData) {
183 this.pdf.doc = new jsPDF(
184 "l",
185 "mm",
186 this.templateData.properties.paperSize
187 );
188 // pdf width and height in millimeter (landscape)
189 this.pdf.width =
190 this.templateData.properties.paperSize === "a3" ? 420 : 297;
191 this.pdf.height =
192 this.templateData.properties.paperSize === "a3" ? 297 : 210;
193 // default values if some are missing in template
194 let defaultFontSize = 11,
195 defaultColor = "black",
196 defaultWidth = 70,
197 defaultTextColor = "black",
198 defaultBorderColor = "white",
199 defaultBgColor = "white",
200 defaultRounding = 2,
201 defaultPadding = 2,
202 defaultOffset = { x: 0, y: 0 };
203 this.templateData.elements.forEach(e => {
204 switch (e.type) {
205 case "diagram": {
206 this.addDiagram(
207 e.position,
208 e.offset || defaultOffset,
209 e.width,
210 e.height
211 );
212 break;
213 }
214 case "diagramtitle": {
215 this.addDiagramTitle(e.position, e.offset, e.fontsize, e.color);
216 break;
217 }
218 case "diagramlegend": {
219 this.addDiagramLegend(
220 e.position,
221 e.offset || defaultOffset,
222 e.color || defaultColor
223 );
224 break;
225 }
226 case "image": {
227 this.addImage(
228 e.url,
229 e.format,
230 e.position,
231 e.offset || defaultOffset,
232 e.width,
233 e.height
234 );
235 break;
236 }
237 case "text": {
238 this.addText(
239 e.position,
240 e.offset || defaultOffset,
241 e.width || defaultWidth,
242 e.fontSize || defaultFontSize,
243 e.color || defaultTextColor,
244 e.text
245 );
246 break;
247 }
248 case "box": {
249 this.addBox(
250 e.position,
251 e.offset,
252 e.width,
253 e.height,
254 e.rounding === 0 || e.rounding ? e.rounding : defaultRounding,
255 e.color || defaultBgColor,
256 e.brcolor || defaultBorderColor
257 );
258 break;
259 }
260 case "textbox": {
261 this.addTextBox(
262 e.position,
263 e.offset || defaultOffset,
264 e.width,
265 e.height,
266 e.rounding === 0 || e.rounding ? e.rounding : defaultRounding,
267 e.padding || defaultPadding,
268 e.fontSize || defaultFontSize,
269 e.color || defaultTextColor,
270 e.background || defaultBgColor,
271 e.text,
272 e.brcolor || defaultBorderColor
273 );
274 break;
275 }
276 }
277 });
278 }
279 this.pdf.doc.save(
280 this.selectedGauge.properties.objname +
281 " Hydrological-condition Diagram.pdf"
282 );
283 },
284 addDiagram(position, offset, width, height) {
285 let x = offset.x,
286 y = offset.y;
125 var svg = document.getElementById(this.containerId).innerHTML; 287 var svg = document.getElementById(this.containerId).innerHTML;
126 if (svg) { 288 if (svg) {
127 svg = svg.replace(/\r?\n|\r/g, "").trim(); 289 svg = svg.replace(/\r?\n|\r/g, "").trim();
128 } 290 }
129 var pdf = new jsPDF("l", "mm", "a3");
130 var canvas = document.createElement("canvas"); 291 var canvas = document.createElement("canvas");
131 canvas.width = window.innerWidth; 292 canvas.width = window.innerWidth;
132 canvas.height = window.innerHeight / 2; 293 canvas.height = window.innerHeight / 2;
133 canvg(canvas, svg, { 294 canvg(canvas, svg, {
134 ignoreMouse: true, 295 ignoreMouse: true,
135 ignoreAnimation: true, 296 ignoreAnimation: true,
136 ignoreDimensions: true 297 ignoreDimensions: true
137 }); 298 });
138 var imgData = canvas.toDataURL("image/png"); 299 var imgData = canvas.toDataURL("image/png");
139 pdf.addImage(imgData, "PNG", 40, 60, 380, 130); 300 // landscape format is used for both a3,a4 papersize
140 this.addDiagramTitle(pdf, 108, 30, 22, "steelblue"); 301 if (!width) {
141 this.addDiagramLegend(pdf, 60, 190, "black"); 302 width = this.templateData.properties.paperSize === "a3" ? 380 : 290;
142 pdf.save( 303 }
143 this.selectedGauge.properties.objname + 304 if (!height) {
144 " Hydrological-condition Diagram.pdf" 305 height = this.templateData.properties.paperSize === "a3" ? 130 : 100;
145 ); 306 }
307 if (["topright", "bottomright"].indexOf(position) !== -1) {
308 x = this.pdf.width - offset.x - width;
309 }
310 if (["bottomright", "bottomleft"].indexOf(position) !== -1) {
311 y = this.pdf.height - offset.y - height;
312 }
313 this.pdf.doc.addImage(imgData, "PNG", x, y, width, height);
314 },
315 applyChange() {
316 if (this.form.template.hasOwnProperty("properties")) {
317 this.templateData = this.defaultTemplate;
318 return;
319 }
320 if (this.form.template) {
321 HTTP.get("/templates/print/" + this.form.template.name, {
322 headers: {
323 "X-Gemma-Auth": localStorage.getItem("token"),
324 "Content-type": "text/xml; charset=UTF-8"
325 }
326 })
327 .then(response => {
328 this.templateData = response.data.template_data;
329 this.form.paperSize = this.templateData.properties.paperSize;
330 })
331 .catch(e => {
332 const { status, data } = e.response;
333 displayError({
334 title: this.$gettext("Backend Error"),
335 message: `${status}: ${data.message || data}`
336 });
337 });
338 }
146 }, 339 },
147 // Gauge info as title 340 // Gauge info as title
148 addDiagramTitle(pdf, x, y, size, color) { 341 addDiagramTitle(position, offset, size, color) {
342 let x = offset.x,
343 y = offset.y;
149 let gaugeInfo = 344 let gaugeInfo =
150 this.selectedGauge.properties.objname + 345 this.selectedGauge.properties.objname +
151 " (" + 346 " (" +
152 this.selectedGauge.id 347 this.selectedGauge.id
153 .split(".")[1] 348 .split(".")[1]
154 .replace(/[()]/g, "") 349 .replace(/[()]/g, "")
155 .split(",")[3] + 350 .split(",")[3] +
156 "): Hydrological Conditions " + 351 "): Hydrological Conditions " +
157 this.longtermInterval.join(" - "); 352 this.longtermInterval.join(" - ");
158 pdf.setTextColor(color); 353 let width =
159 pdf.setFontSize(22); 354 (this.pdf.doc.getStringUnitWidth(gaugeInfo) * size) / (72 / 25.6) +
160 pdf.setFontStyle("bold"); 355 size / 2;
161 pdf.text(gaugeInfo, 108, 30); 356 // if position is on the right, x needs to be calculate with pdf width and
357 // the size of the element
358 if (["topright", "bottomright"].indexOf(position) !== -1) {
359 x = this.pdf.width - offset.x - width;
360 }
361 if (["bottomright", "bottomleft"].indexOf(position) !== -1) {
362 y = this.pdf.height - offset.y - this.getTextHeight(1);
363 }
364 this.pdf.doc.setTextColor(color);
365 this.pdf.doc.setFontSize(size);
366 this.pdf.doc.setFontStyle("bold");
367 this.pdf.doc.text(gaugeInfo, x, y, { baseline: "hanging" });
368 },
369 getTextHeight(numberOfLines) {
370 return (
371 numberOfLines *
372 ((this.pdf.doc.getFontSize() * 25.4) / 80) *
373 this.pdf.doc.getLineHeightFactor()
374 );
162 }, 375 },
163 // Diagram legend 376 // Diagram legend
164 addDiagramLegend(pdf, x, y, color) { 377 addDiagramLegend(position, offset, color) {
165 pdf.setFontSize(10); 378 let x = offset.x,
166 pdf.setTextColor(color); 379 y = offset.y;
167 pdf.setDrawColor("white"); 380 let width =
168 pdf.setFillColor("red"); 381 (this.pdf.doc.getStringUnitWidth("Long-term Amplitude") * 10) /
169 pdf.circle(x, y, 2, "FD"); 382 (72 / 25.6) +
170 pdf.text(x + 3, y + 1, "" + this.yearCompare); 383 5;
171 pdf.setFillColor("orange"); 384 // if position is on the right, x needs to be calculate with pdf width and
172 pdf.circle(x, y + 5, 2, "FD"); 385 // the size of the element
173 pdf.text(x + 3, y + 6, "Q25%"); 386 if (["topright", "bottomright"].indexOf(position) !== -1) {
174 pdf.setFillColor("black"); 387 x = this.pdf.width - offset.x - width;
175 pdf.circle(x, y + 10, 2, "FD"); 388 }
176 pdf.text(x + 3, y + 11, "Median "); 389 if (["bottomright", "bottomleft"].indexOf(position) !== -1) {
177 pdf.setFillColor("purple"); 390 y = this.pdf.height - offset.y - this.getTextHeight(4);
178 pdf.circle(x, y + 15, 2, "FD"); 391 }
179 pdf.text(x + 3, y + 16, "Q75%"); 392 this.pdf.doc.setFontSize(10);
180 pdf.setFillColor("lightsteelblue"); 393 this.pdf.doc.setTextColor(color);
181 pdf.circle(x, y + 20, 2, "FD"); 394 this.pdf.doc.setDrawColor("white");
182 pdf.text(x + 3, y + 21, "Long-term Amplitude"); 395 this.pdf.doc.setFillColor("red");
396 this.pdf.doc.circle(x, y, 2, "FD");
397 this.pdf.doc.text(x + 3, y + 1, "" + this.yearCompare);
398 this.pdf.doc.setFillColor("orange");
399 this.pdf.doc.circle(x, y + 5, 2, "FD");
400 this.pdf.doc.text(x + 3, y + 6, "Q25%");
401 this.pdf.doc.setFillColor("black");
402 this.pdf.doc.circle(x, y + 10, 2, "FD");
403 this.pdf.doc.text(x + 3, y + 11, "Median ");
404 this.pdf.doc.setFillColor("purple");
405 this.pdf.doc.circle(x, y + 15, 2, "FD");
406 this.pdf.doc.text(x + 3, y + 16, "Q75%");
407 this.pdf.doc.setFillColor("lightsteelblue");
408 this.pdf.doc.circle(x, y + 20, 2, "FD");
409 this.pdf.doc.text(x + 3, y + 21, "Long-term Amplitude");
183 }, 410 },
184 drawDiagram() { 411 drawDiagram() {
185 // remove old diagram 412 // remove old diagram
186 d3.select("#" + this.containerId + " svg").remove(); 413 d3.select("#" + this.containerId + " svg").remove();
187 if (!this.selectedGauge || !this.longtermWaterlevels.length) return; 414 if (!this.selectedGauge || !this.longtermWaterlevels.length) return;
845 created() { 1072 created() {
846 window.addEventListener("resize", debounce(this.drawDiagram), 100); 1073 window.addEventListener("resize", debounce(this.drawDiagram), 100);
847 }, 1074 },
848 mounted() { 1075 mounted() {
849 this.drawDiagram(); 1076 this.drawDiagram();
1077 this.templates[0] = this.defaultTemplate;
1078 this.form.template = this.templates[0];
1079 this.templateData = this.form.template;
1080 HTTP.get("/templates/print", {
1081 headers: {
1082 "X-Gemma-Auth": localStorage.getItem("token"),
1083 "Content-type": "text/xml; charset=UTF-8"
1084 }
1085 })
1086 .then(response => {
1087 if (response.data.length) {
1088 this.templates = response.data;
1089 this.form.template = this.templates[0];
1090 this.templates[this.templates.length] = this.defaultTemplate;
1091 this.applyChange();
1092 }
1093 })
1094 .catch(e => {
1095 const { status, data } = e.response;
1096 displayError({
1097 title: this.$gettext("Backend Error"),
1098 message: `${status}: ${data.message || data}`
1099 });
1100 });
850 }, 1101 },
851 updated() { 1102 updated() {
852 this.drawDiagram(); 1103 this.drawDiagram();
853 }, 1104 },
854 destroyed() { 1105 destroyed() {