Skip to content

Commit

Permalink
Merge pull request #251 from KNMI/outlines-barbs-and-text
Browse files Browse the repository at this point in the history
Outlines barbs and text
  • Loading branch information
maartenplieger authored Jun 30, 2023
2 parents 597721f + 56308e1 commit d3d69ec
Show file tree
Hide file tree
Showing 54 changed files with 1,042 additions and 369 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"valarray": "cpp",
"variant": "cpp",
"shared_mutex": "cpp",
"regex": "cpp"
"regex": "cpp",
"numbers": "cpp"
},
"cmake.buildDirectory": "${workspaceFolder}/bin",
"cmake.configureOnOpen": true,
Expand Down
2 changes: 1 addition & 1 deletion CCDFDataModel/CCDFPNGIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ class BoundingBox {
double east;
double west;
};
#define pi 3.141592654
#define pi M_PI

class P {
public:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ USER root
LABEL maintainer="adaguc@knmi.nl"

# Version should be same as in Definitions.h
LABEL version="2.10.4"
LABEL version="2.10.5"

######### First stage (build) ############

Expand Down
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
**Version 2.10.5 2023-06-02**
- Contourlines can have dashes
- Contour text can have an outline
- Windbarbs have a white outline

**Version 2.10.4 2023-06-01**
- Fix: Racmo datasets with rotated_pole projection does work again

Expand Down
213 changes: 212 additions & 1 deletion adagucserverEC/CCairoPlotter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#ifdef ADAGUC_USE_CAIRO
// #define MEASURETIME

#include <cairo-ft.h>
#include "CStopWatch.h"
const char *CCairoPlotter::className = "CCairoPlotter";

Expand Down Expand Up @@ -323,7 +324,7 @@ int CCairoPlotter::_drawFreeTypeText(int x, int y, int &w, int &h, float angle,
pen.y = (my_target_height - y) * 64;
bool c3seen = false;
/* Using the 8859-15 standard */
for (n = 0; n < num_chars; n++) { /* set transformation */
for (n = 0; n < num_chars; n++) { /* set transformation */

FT_Set_Transform(face, &matrix, &pen); /* load glyph image into the slot (erase previous one) */

Expand Down Expand Up @@ -535,11 +536,19 @@ void CCairoPlotter::lineTo(float x1, float y1, float width) {
cairo_set_line_width(cr, width);
cairo_line_to(cr, x1 + 0.5, y1 + 0.5);
}

void CCairoPlotter::endLine() {
cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER);
cairo_stroke(cr);
}

void CCairoPlotter::endLine(const double *dashes, int num_dashes) {
cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER);
cairo_set_dash(cr, dashes, num_dashes, 0);
cairo_stroke(cr);
cairo_set_dash(cr, 0, 0, 0);
}

void CCairoPlotter::line(float x1, float y1, float x2, float y2) {
cairo_set_source_rgba(cr, rr, rg, rb, ra);
cairo_move_to(cr, x1 + 0.5, y1 + 0.5);
Expand Down Expand Up @@ -653,6 +662,56 @@ void CCairoPlotter::drawText(int x, int y, double angle, const char *text) {
_drawFreeTypeText(x, y, w, h, angle, text, true);
}

void CCairoPlotter::drawStrokedText(int x, int y, double angle, const char *text, float fontSize, float strokeWidth, CColor bgcolor, CColor fgcolor) {
if (library == NULL) {
int status = initializeFreeType();
if (status != 0) {
// TODO
}
}

cairo_save(cr);

cairo_font_face_t *ct = cairo_ft_font_face_create_for_ft_face(face, 0);
cairo_set_font_face (cr, ct);
cairo_set_font_size(cr, fontSize);

// Save the current path, because we might be drawing something like contour lines, which should not be stroked.
cairo_path_t *cp = cairo_copy_path(cr);

cairo_new_path(cr);
cairo_set_dash(cr, 0, 0, 0);

cairo_move_to(cr, x, y);
cairo_rotate(cr, angle);
cairo_text_path(cr, text);
if (strokeWidth > 0) {
cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba(cr, bgcolor.r / 255., bgcolor.g / 255., bgcolor.b / 255., .2);
cairo_set_line_width(cr, 2.5 + strokeWidth);
cairo_stroke_preserve(cr);

cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
cairo_set_source_rgba(cr, bgcolor.r / 255., bgcolor.g / 255., bgcolor.b / 255., 1);
cairo_set_line_width(cr, 1.5 + strokeWidth);
cairo_stroke_preserve(cr);
}

cairo_set_antialias(cr, CAIRO_ANTIALIAS_BEST);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
cairo_set_source_rgba(cr, fgcolor.r / 255., fgcolor.g / 255., fgcolor.b / 255., 1);
cairo_fill(cr);

cairo_close_path(cr);

cairo_restore(cr);
// Put the original path back
cairo_append_path(cr, cp);
cairo_path_destroy(cp);
}

void CCairoPlotter::writeToPng24Stream(FILE *fp, unsigned char) { writeARGBPng(width, height, ARGBByteBuffer, fp, 24, false); }

void CCairoPlotter::writeToPng8Stream(FILE *fp, unsigned char, bool use8bitpalAlpha) { writeARGBPng(width, height, ARGBByteBuffer, fp, 8, use8bitpalAlpha); }
Expand Down Expand Up @@ -1044,3 +1103,155 @@ void CCairoPlotter::writeToWebP32Stream(FILE *fp, unsigned char, int quality) {
}

#endif

static int drawBarbTriangle(cairo_t *cr, int x, int y, int nPennants, double direction, double shaftLength, double barbLengthWithFlip, int nrPos) {
int pos = 0;
double dx1 = cos(direction) * (shaftLength);
double dy1 = sin(direction) * (shaftLength);
double wx1 = double(x) - dx1;
double wy1 = double(y) + dy1; // wind barb top (flag side)

for (int i = 0; i < nPennants; i++) {
double wx3 = wx1 + pos * dx1 / nrPos;
double wy3 = wy1 - pos * dy1 / nrPos;
pos++;
double hx3 = wx1 + pos * dx1 / nrPos + cos(M_PI + direction + M_PI / 2) * barbLengthWithFlip;
double hy3 = wy1 - pos * dy1 / nrPos - sin(M_PI + direction + M_PI / 2) * barbLengthWithFlip;
pos++;
double wx4 = wx1 + pos * dx1 / nrPos;
double wy4 = wy1 - pos * dy1 / nrPos;

double ptx[3] = {wx3, hx3, wx4};
double pty[3] = {wy3, hy3, wy4};

cairo_move_to(cr, ptx[0], pty[0]);
for (int j = 1; j < 3; j++) {
cairo_line_to(cr, ptx[j], pty[j]);
}
}
return pos;
}

void CCairoPlotter::drawBarb(int x, int y, double direction, double strength, CColor barbColor, CColor outlineColor, bool drawOutline, float lineWidth, bool toKnots, bool flip, bool drawText) {
// Barb settings
float centerDiscRadius = 3;
int shaftLength = 37;
int barbLength = 12;
int halfBarbLength = 6;

// Preserve path
cairo_save(cr);
cairo_path_t *cp = cairo_copy_path(cr);
cairo_new_path(cr);
cairo_set_line_width(cr, lineWidth);
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);

int strengthInKnots = round(strength);
if (toKnots) {
strengthInKnots = round(strength * 3600 / 1852.);
}

// Rounded to the nearest 5 kts
int strengthInKnotsRoundedToFive = round((strengthInKnots + 2) / 5) * 5;

int nPennants = strengthInKnotsRoundedToFive / 50;
int nBarbs = (strengthInKnotsRoundedToFive % 50) / 10 + 0.5;
bool hasHalfBarb = strengthInKnotsRoundedToFive % 10 >= 5;
float flipFactor = flip ? -1 : 1;
int barbLengthWithFlip = int(-barbLength * flipFactor);
int halfBarbLengthWithFlip = int(-halfBarbLength * flipFactor);

if (strengthInKnotsRoundedToFive <= 2) {
// https://www.weather.gov/hfo/windbarbinfo
// When wind speeds are 2 kts or less, a small open circle is used.
cairo_arc(cr, x, y, 6, 0, 2 * M_PI);
} else {

double dx1 = cos(direction) * (shaftLength);
double dy1 = sin(direction) * (shaftLength);

double wx1 = double(x) - dx1;
double wy1 = double(y) + dy1; // wind barb top (flag side)

// Draw small center circle
cairo_arc(cr, x, y, centerDiscRadius, 0, 2 * M_PI);

// Draw main shaft from center to end
cairo_move_to(cr, wx1, wy1);
cairo_line_to(cr, x - cos(direction) * (centerDiscRadius), y + sin(direction) * (centerDiscRadius));

// Draw flags
int nrPos = 10;
int pos = drawBarbTriangle(cr, x, y, nPennants, direction, shaftLength, barbLengthWithFlip, nrPos);

// Draw full Barb
if (nPennants > 0) pos++;
for (int i = 0; i < nBarbs; i++) {
double wx3 = wx1 + pos * dx1 / nrPos;
double wy3 = wy1 - pos * dy1 / nrPos;
double hx3 = wx3 - cos(M_PI / 2 - direction + (2 - float(flipFactor) * 0.1) * M_PI / 2) * barbLengthWithFlip; // was: +cos
double hy3 = wy3 - sin(M_PI / 2 - direction + (2 - float(flipFactor) * 0.1) * M_PI / 2) * barbLengthWithFlip; // was: -sin
cairo_move_to(cr, wx3, wy3);
cairo_line_to(cr, hx3, hy3);
pos++;
}

if ((nPennants + nBarbs) == 0) pos++;

// Draw half Barb
if (hasHalfBarb) {
double wx3 = wx1 + pos * dx1 / nrPos;
double wy3 = wy1 - pos * dy1 / nrPos;
double hx3 = wx3 - cos(M_PI / 2 - direction + (2 - float(flipFactor) * 0.1) * M_PI / 2) * halfBarbLengthWithFlip;
double hy3 = wy3 - sin(M_PI / 2 - direction + (2 - float(flipFactor) * 0.1) * M_PI / 2) * halfBarbLengthWithFlip;

cairo_move_to(cr, wx3, wy3);
cairo_line_to(cr, hx3, hy3);
pos++;
}
}
// No dash
cairo_set_dash(cr, 0, 0, 0);

if (drawOutline) {
cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba(cr, outlineColor.r / 255., outlineColor.g / 255., outlineColor.b / 255., .2);
cairo_set_line_width(cr, 5.5);
cairo_stroke_preserve(cr);

cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
cairo_set_source_rgba(cr, outlineColor.r / 255., outlineColor.g / 255., outlineColor.b / 255., 1);
cairo_set_line_width(cr, 4.5);
cairo_stroke_preserve(cr);
}

// Stroke thin version
cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
cairo_set_source_rgba(cr, barbColor.r / 255., barbColor.g / 255., barbColor.b / 255., 1);
cairo_set_line_width(cr, lineWidth);
cairo_stroke(cr);

if (strengthInKnotsRoundedToFive > 2) {
drawBarbTriangle(cr, x, y, nPennants, direction, shaftLength, barbLengthWithFlip, 10);
cairo_close_path(cr);
cairo_set_source_rgba(cr, barbColor.r / 255., barbColor.g / 255., barbColor.b / 255., 1);
cairo_fill_preserve(cr);
}

// End end restore
cairo_close_path(cr);
cairo_restore(cr);
cairo_append_path(cr, cp);
cairo_path_destroy(cp);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);

CT::string text;
text.print("%d", strengthInKnots);
if (drawText) {
this->drawStrokedText(x - cos(direction + M_PI) * 15 - 5, y + sin(direction + M_PI) * 12 + 5, 0, text.c_str(), 12, 1 * drawOutline, outlineColor, barbColor);
}
}
4 changes: 4 additions & 0 deletions adagucserverEC/CCairoPlotter.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include FT_FREETYPE_H
#include <stdio.h>
#include <math.h>
#include "CColor.h"

#include "COctTreeColorQuantizer.h"

Expand Down Expand Up @@ -108,6 +109,7 @@ class CCairoPlotter {
void lineTo(float x1, float y1);
void lineTo(float x1, float y1, float width);
void endLine();
void endLine(const double *dashes, int num_dashes);
void line(float x1, float y1, float x2, float y2);
void line(float x1, float y1, float x2, float y2, float width);
void circle(int x, int y, int r);
Expand All @@ -117,12 +119,14 @@ class CCairoPlotter {
void poly(float x[], float y[], int n, bool closePath, bool fill);
void poly(float x[], float y[], int n, float lineWidth, bool closePath, bool fill);
void drawText(int x, int y, double angle, const char *text);
void drawStrokedText(int x, int y, double angle, const char *text, float fontSize, float strokeWidth, CColor bgcolor, CColor fgcolor);

void writeToPng8Stream(FILE *fp, unsigned char alpha, bool use8bitpalAlpha);
void writeToPng24Stream(FILE *fp, unsigned char alpha);
void writeToPng32Stream(FILE *fp, unsigned char alpha);
void writeToWebP32Stream(FILE *fp, unsigned char alpha, int quality);
void setToSurface(cairo_surface_t *png);
void drawBarb(int x, int y, double direction, double strength, CColor color, CColor outlineColor, bool drawOutline, float lineWidth, bool toKnots, bool flip, bool drawText);
};

#endif /* CCAIROPLOTTER_H_ */
Expand Down
21 changes: 12 additions & 9 deletions adagucserverEC/CColor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* Project: ADAGUC Server
* Purpose: ADAGUC OGC Server
* Author: Maarten Plieger, plieger "at" knmi.nl
* Date: 2013-06-01
* Date: 2022-06-30
*
******************************************************************************
*
* Copyright 2013, Royal Netherlands Meteorological Institute (KNMI)
* Copyright 2022, Royal Netherlands Meteorological Institute (KNMI)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,10 +22,13 @@
* limitations under the License.
*
******************************************************************************/
#ifndef CColor_H
#define CColor_H
#include <stdlib.h>
#include <CServerConfig_CPPXSD.h>
#include <stdio.h>
#include <string.h>

#define CSERVER_HEXDIGIT_TO_DEC(DIGIT) (DIGIT > 96 ? DIGIT - 87 : DIGIT > 64 ? DIGIT - 55 : DIGIT - 48) // Converts "9" to 9, "A" to 10 and "a" to 10

#ifndef CCOLOR_H
#define CCOLOR_H
class CColor {
public:
unsigned char r, g, b, a;
Expand All @@ -42,10 +45,10 @@ class CColor {
this->a = a;
}
CColor(const char *color) { parse(color); }
/**
* color can have format #RRGGBB or #RRGGBBAA
*/
void parse(const char *color) {
/**
* color can have format #RRGGBB or #RRGGBBAA
*/
size_t l = strlen(color);

if (l == 7 && color[0] == '#') {
Expand Down
Loading

0 comments on commit d3d69ec

Please sign in to comment.