Skip to content

Instantly share code, notes, and snippets.

@norecords
Last active October 4, 2022 08:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save norecords/85ea0186497404618574a7c62202f304 to your computer and use it in GitHub Desktop.
Save norecords/85ea0186497404618574a7c62202f304 to your computer and use it in GitHub Desktop.
Highcharts Meteogram for Belchertown skin and Weewx
<!--
*
* Highcharts Meteogram for Belchertown skin and Weewx.
* Working with Highstock.js, Dark mode and forecast translations from skin.conf
* Download source code at https://gist.github.com/norecords/85ea0186497404618574a7c62202f304
*
-->
<div id="meteogram-container"></div>
<script src="https://code.highcharts.com/modules/windbarb.js"></script>
<script src="https://code.highcharts.com/modules/pattern-fill.js"></script>
<script type="text/javascript">
/**
* From https://www.highcharts.com/demo/combo-meteogram
* This is what it does:
*
* - Loads weather forecast from www.yr.no in form of a JSON service.
* - When the data arrives async, a Meteogram instance is created. We have
* created the Meteogram prototype to provide an organized structure of the
* different methods and subroutines associated with the demo.
* - The parseYrData method parses the data from www.yr.no into several parallel
* arrays. These arrays are used directly as the data option for temperature,
* precipitation and air pressure.
* - After this, the options structure is built, and the chart generated with
* the parsed data.
* - On chart load, weather icons and the frames for the wind arrows are
* rendered using custom logic.
*/
// Edit next line with your latitude, longitude and altitude
const url = 'https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=43.487&lon=6.079&altitude=164';
// Edit the chart title
const chartText = 'Météogramme pour Correns, France';
// Edit next line to translate series name in the tooltip
const tooltipSeriesName = ["Temperature", "Precipitation", "Air pressure", "Wind"];
function Meteogram(json, container) {
// Parallel arrays for the chart data, these are populated as the JSON file
// is loaded
this.symbols = [];
this.precipitations = [];
this.precipitationsError = []; // Only for some data sets
this.winds = [];
this.temperatures = [];
this.pressures = [];
// Initialize
this.json = json;
this.container = container;
// Run
this.parseYrData();
}
/**
* Mapping of the symbol code in yr.no's API to the icons in their public
* GitHub repo, as well as the text used in the tooltip.
*
* https://api.met.no/weatherapi/weathericon/2.0/documentation
*/
Meteogram.dictionary = {
clearsky: {
symbol: '01',
text: '$obs.label.forecast_cloud_code_CL'
},
fair: {
symbol: '02',
text: '$obs.label.forecast_cloud_code_FW'
},
partlycloudy: {
symbol: '03',
text: '$obs.label.forecast_cloud_code_SC'
},
cloudy: {
symbol: '04',
text: '$obs.label.forecast_cloud_code_OV'
},
lightrainshowers: {
symbol: '40',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_RW'
},
rainshowers: {
symbol: '05',
text: '$obs.label.forecast_weather_code_RW'
},
heavyrainshowers: {
symbol: '41',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_RW'
},
lightrainshowersandthunder: {
symbol: '24',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_RW / $obs.label.forecast_weather_code_T'
},
rainshowersandthunder: {
symbol: '06',
text: '$obs.label.forecast_weather_code_RW / $obs.label.forecast_weather_code_T'
},
heavyrainshowersandthunder: {
symbol: '25',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_RW / $obs.label.forecast_weather_code_T'
},
lightsleetshowers: {
symbol: '42',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_IP'
},
sleetshowers: {
symbol: '07',
text: '$obs.label.forecast_weather_code_IP'
},
heavysleetshowers: {
symbol: '43',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_IP'
},
lightsleetshowersandthunder: {
symbol: '26',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_IP $obs.label.forecast_weather_code_T'
},
sleetshowersandthunder: {
symbol: '20',
text: '$obs.label.forecast_weather_code_IP / $obs.label.forecast_weather_code_T'
},
heavysleetshowersandthunder: {
symbol: '27',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_IP / $obs.label.forecast_weather_code_T'
},
lightsnowshowers: {
symbol: '44',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_SW'
},
snowshowers: {
symbol: '08',
text: '$obs.label.forecast_weather_code_SW'
},
heavysnowshowers: {
symbol: '45',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_SW'
},
lightsnowshowersandthunder: {
symbol: '28',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_SW / $obs.label.forecast_weather_code_T'
},
snowshowersandthunder: {
symbol: '21',
text: '$obs.label.forecast_weather_code_SW / $obs.label.forecast_weather_code_T'
},
heavysnowshowersandthunder: {
symbol: '29',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_SW / $obs.label.forecast_weather_code_T'
},
lightrain: {
symbol: '46',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_R'
},
rain: {
symbol: '09',
text: '$obs.label.forecast_weather_code_R'
},
heavyrain: {
symbol: '10',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_R'
},
lightrainandthunder: {
symbol: '30',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_R / $obs.label.forecast_weather_code_T'
},
rainandthunder: {
symbol: '22',
text: '$obs.label.forecast_weather_code_R / $obs.label.forecast_weather_code_T'
},
heavyrainandthunder: {
symbol: '11',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_R / $obs.label.forecast_weather_code_T'
},
lightsleet: {
symbol: '47',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_IP'
},
sleet: {
symbol: '12',
text: '$obs.label.forecast_weather_code_IP'
},
heavysleet: {
symbol: '48',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_IP'
},
lightsleetandthunder: {
symbol: '31',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_IP / $obs.label.forecast_weather_code_T'
},
sleetandthunder: {
symbol: '23',
text: '$obs.label.forecast_weather_code_IP / $obs.label.forecast_weather_code_T'
},
heavysleetandthunder: {
symbol: '32',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_IP / $obs.label.forecast_weather_code_T'
},
lightsnow: {
symbol: '49',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_S'
},
snow: {
symbol: '13',
text: '$obs.label.forecast_weather_code_S'
},
heavysnow: {
symbol: '50',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_S'
},
lightsnowandthunder: {
symbol: '33',
text: '$obs.label.forecast_intensity_code_L $obs.label.forecast_weather_code_S / $obs.label.forecast_weather_code_T'
},
snowandthunder: {
symbol: '14',
text: '$obs.label.forecast_weather_code_S / $obs.label.forecast_weather_code_T'
},
heavysnowandthunder: {
symbol: '34',
text: '$obs.label.forecast_intensity_code_H $obs.label.forecast_weather_code_S / $obs.label.forecast_weather_code_T'
},
fog: {
symbol: '15',
text: '$obs.label.forecast_weather_code_F'
}
};
/**
* Translate beaufort names
*/
Highcharts.seriesTypes.windbarb.prototype.beaufortName = ["$beaufort0", "$beaufort1", "$beaufort2", "$beaufort3",
"$beaufort4", "$beaufort5", "$beaufort6", "$beaufort7", "$beaufort9", "$beaufort10","$beaufort11", "$beaufort12"];
/**
* Draw the weather symbols on top of the temperature series. The symbols are
* fetched from yr.no's MIT licensed weather symbol collection.
* https://github.com/YR/weather-symbols
*/
Meteogram.prototype.drawWeatherSymbols = function (chart) {
chart.series[0].data.forEach((point, i) => {
if (this.resolution > 36e5 || i % 2 === 0) {
const [symbol, specifier] = this.symbols[i].split('_'),
icon = Meteogram.dictionary[symbol].symbol +
({ day: 'd', night: 'n' }[specifier] || '');
if (Meteogram.dictionary[symbol]) {
chart.renderer
.image(
'https://cdn.jsdelivr.net/gh/nrkno/yr-weather-symbols' +
`@8.0.1/dist/svg/` + icon + `.svg`,
point.plotX + chart.plotLeft - 8,
point.plotY + chart.plotTop - 35,
30,
30
)
.attr({
zIndex: 5
})
.add();
} else {
console.log(symbol);
}
}
});
};
/**
* Draw blocks around wind arrows, below the plot area
*/
Meteogram.prototype.drawBlocksForWindArrows = function (chart) {
const xAxis = chart.xAxis[0];
for (
let pos = xAxis.min, max = xAxis.max, i = 0;
pos <= max + 36e5; pos += 36e5,
i += 1
) {
// Get the X position
const isLast = pos === max + 36e5,
x = Math.round(xAxis.toPixels(pos)) + (isLast ? 0.5 : -0.5);
// Draw the vertical dividers and ticks
const isLong = this.resolution > 36e5 ?
pos % this.resolution === 0 :
i % 2 === 0;
chart.renderer
.path([
'M', x, chart.plotTop + chart.plotHeight + (isLong ? 0 : 28),
'L', x, chart.plotTop + chart.plotHeight + 32,
'Z'
])
.attr({
stroke: chart.options.chart.plotBorderColor,
'stroke-width': 1
})
.add();
}
// Center items in block
chart.get('windbarbs').markerGroup.attr({
translateX: chart.get('windbarbs').markerGroup.translateX + 8
});
};
/**
* Build and return the Highcharts options structure
*/
Meteogram.prototype.getChartOptions = function () {
return {
chart: {
renderTo: this.container,
marginBottom: 70,
marginRight: 40,
marginTop: 50,
plotBorderWidth: 1,
width: 1100,
height: 310,
alignTicks: false
},
defs: {
patterns: [{
id: 'precipitation-error',
path: {
d: [
'M', 3.3, 0, 'L', -6.7, 10,
'M', 6.7, 0, 'L', -3.3, 10,
'M', 10, 0, 'L', 0, 10,
'M', 13.3, 0, 'L', 3.3, 10,
'M', 16.7, 0, 'L', 6.7, 10
].join(' '),
stroke: '#68CFE8',
strokeWidth: 1
}
}]
},
title: {
text: chartText,
align: 'left',
style: {
whiteSpace: 'nowrap',
textOverflow: 'ellipsis'
}
},
credits: {
text: 'Forecast from <a href="https://yr.no">yr.no</a>',
href: 'https://yr.no',
position: {
x: -40
}
},
tooltip: {
shared: true,
useHTML: true,
headerFormat:
'<small>{point.x:%A %e %b, %H:%M} - {point.to:%H:%M}</small><br>' +
'<b>{point.symbolName}</b><br>'
},
exporting: {
enabled: false
},
xAxis: [{ // Bottom X axis
type: 'datetime',
tickInterval: 2 * 36e5, // two hours
minorTickInterval: 36e5, // one hour
tickLength: 0,
gridLineWidth: 1,
gridLineColor: 'rgba(128, 128, 128, 0.1)',
startOnTick: false,
endOnTick: false,
minPadding: 0,
maxPadding: 0,
offset: 30,
showLastLabel: true,
labels: {
format: '{value:%H}'
},
crosshair: true
}, { // Top X axis
linkedTo: 0,
type: 'datetime',
tickInterval: 24 * 3600 * 1000,
labels: {
format: '{value:<span style="font-size: 12px; font-weight: bold">%a</span> %e %b}',
align: 'left',
x: 3,
y: 5
},
opposite: true,
tickLength: 20,
gridLineWidth: 1
}],
yAxis: [{ // temperature axis
title: {
text: '$unit.label.outTemp',
offset: 0,
align: 'high',
rotation: 0,
style: {
fontSize: '12px',
},
textAlign: 'left',
x: -17,
y: 2
},
labels: {
style: {
fontSize: '10px'
},
x: -5
},
plotLines: [{ // zero plane
value: 0,
color: '#BBBBBB',
width: 1,
zIndex: 3
}],
maxPadding: 0.3,
minRange: 8,
tickInterval: 1,
gridLineColor: 'rgba(128, 128, 128, 0.1)',
showLastLabel: false
}, { // precipitation axis
title: {
text: null
},
labels: {
enabled: false
},
className: 'dark-gridline',
gridLineWidth: 0,
tickLength: 0,
minRange: 10,
min: 0
}, { // Air pressure
allowDecimals: false,
title: { // Title on top of axis
text: 'hPa',
offset: 0,
align: 'high',
rotation: 0,
style: {
fontSize: '12px',
},
textAlign: 'left',
x: 5
},
labels: {
style: {
fontSize: '8px',
},
y: 2,
x: 5
},
className: 'dark-gridline',
gridLineWidth: 0,
opposite: true,
showLastLabel: false
}],
legend: {
enabled: false
},
plotOptions: {
series: {
pointPlacement: 'between',
states: {
inactive: {
enabled: false
}
}
}
},
series: [{
name: tooltipSeriesName[0],
data: this.temperatures,
type: 'spline',
marker: {
enabled: false,
states: {
hover: {
enabled: true
}
}
},
tooltip: {
pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
'{series.name}: <b>{point.y}°C</b><br/>'
},
zIndex: 1,
color: '#FF3333',
negativeColor: '#48AFE8'
}, {
name: tooltipSeriesName[1],
data: this.precipitationsError,
type: 'column',
color: 'url(#precipitation-error)',
yAxis: 1,
groupPadding: 0,
pointPadding: 0,
tooltip: {
valueSuffix: ' mm',
pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
'{series.name}: <b>{point.minvalue} mm - {point.maxvalue} mm</b><br/>'
},
grouping: false,
dataLabels: {
enabled: this.hasPrecipitationError,
filter: {
operator: '>',
property: 'maxValue',
value: 0
},
style: {
fontSize: '8px',
color: 'gray'
}
}
}, {
name: tooltipSeriesName[1],
data: this.precipitations,
type: 'column',
color: '#68CFE8',
yAxis: 1,
groupPadding: 0,
pointPadding: 0,
grouping: false,
dataLabels: {
enabled: !this.hasPrecipitationError,
filter: {
operator: '>',
property: 'y',
value: 0
},
style: {
fontSize: '8px',
color: 'gray'
}
},
tooltip: {
valueSuffix: ' mm'
}
}, {
name: tooltipSeriesName[2],
color: Highcharts.getOptions().colors[2],
data: this.pressures,
marker: {
enabled: false
},
shadow: false,
tooltip: {
valueSuffix: ' hPa'
},
dashStyle: 'shortdot',
yAxis: 2
}, {
name: tooltipSeriesName[3],
type: 'windbarb',
id: 'windbarbs',
color: '#db6600',
lineWidth: 1.5,
data: this.winds,
vectorLength: 18,
yOffset: -15,
tooltip: {
#if $unit.unit_type.windSpeed == 'km_per_hour'
// Convert wind speed m/s to km/h in the tooltip
valueDecimals: 4,
pointFormatter: function() {
return (
'<span style="color:' + this.series.color + '">\u25CF</span> '
+ this.series.name + ': <b>' + Math.round(this.value * 3.6) + ' km/h</b> '
+ '(' + this.beaufort + ')<br/>'
);
}
#elif $unit.unit_type.windSpeed == 'mile_per_hour'
// Convert wind speed m/s to mph in the tooltip
valueDecimals: 4,
pointFormatter: function() {
return (
'<span style="color:' + this.series.color + '">\u25CF</span> '
+ this.series.name + ': <b>' + Math.round(this.value * 2.237) + ' mph</b> '
+ '(' + this.beaufort + ')<br/>'
);
}
#elif $unit.unit_type.windSpeed == 'knot'
// Convert wind speed m/s to knot in the tooltip
valueDecimals: 4,
pointFormatter: function() {
return (
'<span style="color:' + this.series.color + '">\u25CF</span> '
+ this.series.name + ': <b>' + Math.round(this.value * 1.94384) + ' knt</b> '
+ '(' + this.beaufort + ')<br/>'
);
}
#else
// Default
valueSuffix: ' m/s'
#end if
}
}]
};
};
/**
* Post-process the chart from the callback function, the second argument
* Highcharts.Chart.
*/
Meteogram.prototype.onChartLoad = function (chart) {
this.drawWeatherSymbols(chart);
this.drawBlocksForWindArrows(chart);
};
/**
* Create the chart. This function is called async when the data file is loaded
* and parsed.
*/
Meteogram.prototype.createChart = function () {
this.chart = new Highcharts.Chart(this.getChartOptions(), chart => {
this.onChartLoad(chart);
});
};
Meteogram.prototype.error = function () {
document.getElementById('loading').innerHTML =
'<i class="fa fa-frown-o"></i> Failed loading data, please try again later';
};
/**
* Handle the data. This part of the code is not Highcharts specific, but deals
* with yr.no's specific data format
*/
Meteogram.prototype.parseYrData = function () {
let pointStart;
if (!this.json) {
return this.error();
}
// Loop over hourly (or 6-hourly) forecasts
this.json.properties.timeseries.forEach((node, i) => {
const x = Date.parse(node.time),
nextHours = node.data.next_1_hours || node.data.next_6_hours,
symbolCode = nextHours && nextHours.summary.symbol_code,
to = node.data.next_1_hours ? x + 36e5 : x + 6 * 36e5;
if (to > pointStart + 48 * 36e5) {
return;
}
// Populate the parallel arrays
this.symbols.push(nextHours.summary.symbol_code);
this.temperatures.push({
x,
y: node.data.instant.details.air_temperature,
// custom options used in the tooltip formatter
to,
symbolName: Meteogram.dictionary[
symbolCode.replace(/_(day|night)$/, '')
].text
});
this.precipitations.push({
x,
y: nextHours.details.precipitation_amount
});
if (i % 2 === 0) {
this.winds.push({
x,
value: node.data.instant.details.wind_speed,
direction: node.data.instant.details.wind_from_direction
});
}
this.pressures.push({
x,
y: node.data.instant.details.air_pressure_at_sea_level
});
if (i === 0) {
pointStart = (x + to) / 2;
}
});
// Create the chart when the data is loaded
this.createChart();
};
// End of the Meteogram protype
// On DOM ready...
window.addEventListener("DOMContentLoaded", (event) => {
Highcharts.ajax({
url,
dataType: 'json',
success: json => {
window.meteogram = new Meteogram(json, 'meteogram-container');
},
error: Meteogram.prototype.error,
headers: {
// Override the Content-Type to avoid preflight problems with CORS
'Content-Type': 'text/plain'
}
});
});
</script>
<style type="text/css">
#meteogram-container {
min-width: 340px;
max-width: 1100px;
height: 310px;
margin: 10px auto 10px auto;
overflow-x: auto !important;
}
#meteogram-container .highcharts-text-outline {
stroke: #ffffff;
}
.dark #meteogram-container .highcharts-point {
stroke-width: 1.5px;
}
.dark #meteogram-container .highcharts-text-outline,
.dark #meteogram-container .highcharts-yaxis-grid.dark-gridline path {
stroke:transparent !important;
}
</style>
@dtalens
Copy link

dtalens commented Jan 13, 2022

Thanks @norecords. I'll try it!

@dtalens
Copy link

dtalens commented Jan 13, 2022

Is possible convert wind units to Km/h?. I found this link where it says how to do it but not how to apply it.

@norecords
Copy link
Author

Hi @dtalens this is my TODO LIST.

@norecords
Copy link
Author

norecords commented Jan 16, 2022

@dtalens see the last rev.

@dtalens
Copy link

dtalens commented Jan 16, 2022

Thanks!!

@CaptainSteubing
Copy link

Very nice and great work, thanks. Is there a way to convert the units from metric to imperial?

@dtalens
Copy link

dtalens commented Jul 15, 2022

Hi @norecords, I intend to change the icons on my belchertown website for animated ones. And I would like to change them in the meteogram as well, so I would like the whole web to use the locally saved icons, I see I should change several javasescript functions but I don't know how to do it, could you help me? thanks.

@norecords
Copy link
Author

@CaptainSteubing it could be done by a pointFormatter in the tooltip section like I do for windspeed, I put it in my TODO LIST.

@dtalens you mean Meteocons ? I have to look how they works and if it could be integrate to the meteogram.

@dtalens
Copy link

dtalens commented Jul 15, 2022

Yes, I would like to change the icons to meteocons. Change belchertown js so that it takes the svg it will be done but inside the meteogram I don't know how to do it.

@CaptainSteubing
Copy link

CaptainSteubing commented Jul 15, 2022

it could be done by a pointFormatter in the tooltip section like I do for windspeed, I put it in my TODO LIST.

OK, I'll have a look, thanks for pointing me in the right direction. Keep up the good work!

@norecords
Copy link
Author

@CaptainSteubing After some tries you should convert data on this section

// Populate the parallel arrays
...
    y: node.data.instant.details.air_temperature,
...

to convert data from Celsius to Fahrenheit
y: Math.round((node.data.instant.details.air_temperature*9/5) + 32),

I'll work on it to be done from Weewx units

@CaptainSteubing
Copy link

CaptainSteubing commented Jul 18, 2022

Thanks @norecords. I've made the adjustments as per your suggestion and got everything converted over to imperial. Adjusted the Tooltips accordingly.

I was having problems with the rounding since Math.round rounds to the nearest integer. I wanted to be able to round to a specific number of decimal places.

// Function to round off to specified number of places.

let roundToNoofPlaces = (number, numberplaces) => {
  const x = Math.pow(10,numberplaces);
  return Math.round(num * x) / x;
}

So in the above case I used:

y: roundToNoofPlaces(node.data.instant.details.air_temperature*9/5 + 32,2),

Digging into this is helping me develop an idea I am thinking about with for creating some gauge dashboards (via the hook include files) for the Belchertown Skin. If I can get my idea working, I'll be able to share it with the wider WeeWX community.

@Millardiang
Copy link

Millardiang commented Jul 25, 2022

If anybody has trouble in making the above function work, try this one: -

 function round(number, precision) {
'use strict';
precision = precision ? +precision : 0;

var sNumber     = number + '',
    periodIndex = sNumber.indexOf('.'),
    factor      = Math.pow(10, precision);

if (periodIndex === -1 || precision < 0) {
    return Math.round(number * factor) / factor;
}

number = +number;

// sNumber[periodIndex + precision + 1] is the last digit
if (sNumber[periodIndex + precision + 1] >= 5) {
    // Correcting float error
    // factor * 10 to use one decimal place beyond the precision
    number += (number < 0 ? -1 : 1) / (factor * 10);
}

return +number.toFixed(precision);
}

use it like this: -
y: round((node.data.instant.details.air_temperature*9/5 + 32),2),

@steepleian
Copy link

Yes, I would like to change the icons to meteocons. Change belchertown js so that it takes the svg it will be done but inside the meteogram I don't know how to do it.

Has anyone succeded in converting the icons to Meteocons yet? I would be interested to know as I have had very mixed results myself. Meteocons are running on my main page very happily and not so with the meteogram

@steepleian
Copy link

steepleian commented Jul 27, 2022

I have now achieved this and it is quite straightfoward.

UPDATED

Rather than hack the original code, the simplest way is to re-name the Meteocons to match the name of the YR icon.

For example for the YR weather code 'clearsky_day': -

change the Meteocon file name 'clear-day.svg' to '01d.svg'

Once you have completed the re-naming of the Meteocon files you must make a change to the location of the icons. At line 252 in the code you will find this section: -

  if (Meteogram.dictionary[symbol]) {
    chart.renderer
      .image(
        'https://cdn.jsdelivr.net/gh/nrkno/yr-weather-symbols' +
          `@8.0.1/dist/svg/` + icon + `.svg`,
        point.plotX + chart.plotLeft - 8,
        point.plotY + chart.plotTop - 35,
        30,
        30
      )

You will need to change this to the location of where you have stored the re-named Meteocon files on your own server. In my case it looks like this: -

  if (Meteogram.dictionary[symbol]) {
    chart.renderer
      .image(
        `img/meteocons/` + icon + `.svg`,
        point.plotX + chart.plotLeft - 8,
        point.plotY + chart.plotTop - 30,
        30,
        30
      )
      
      I have this running now on my local server. I will post the link once I move it up to my public server.
      
      Ian

@dtalens
Copy link

dtalens commented Jul 27, 2022

Thanks @steepleian

@steepleian
Copy link

I have created a set of Meteocons mapped to the YR weather symbol file names. You can download them here: -

https://github.com/steepleian/MeteoconsYR/archive/refs/heads/meteoconsYR.zip

@norecords
Copy link
Author

@steepleian just saw that you also change icons for weather34. Thanks for your share Ian

@steepleian
Copy link

steepleian commented Jul 27, 2022

I have been using the meteocons on my test server for a few months now. I am writing a new template as a successor to weewx-Weather34. I had been trying out many designs including some of my own. I thought it would be cool to see how they look on Weather34 as well. If you go to https://claydonsweather.org.uk/weewx/divumdev you can see them in use on the meteogram as well (link is under the forecast module). The units switching is also working very well on the meteogram. You can toggle the units from the menu (under the hamburger button top left).
Ian

@norecords
Copy link
Author

norecords commented Jul 28, 2022

@steepleian You have some issues on your meteogram, first don't convert wind speed with node.data.instant.details otherwise windbarbs will be wrong.
km/h
image
mph
image
They have to stay in m/s. Look at the tooltip section

        pointFormatter: function() {
          return (
            '<span style="color:' + this.series.color + '">\u25CF</span> '
            + this.series.name + ': <b>' + Math.round(this.value * 3.6) + ' km/h</b> '
            + '(' + this.beaufort + ')<br/>' 
          );
        }

second, icons aren't same size from yr.no to meteocon set, so you have to increase the position to not glue the line.
point.plotY + chart.plotTop - 35,
image

Finally if you resize the browser window windbarbs are redrawn but not at the right place, to avoid this you have to fix the size of the chart and have an overflow on X in case the window is smaller
image

#meteogram-container {
  min-width: 340px;
  max-width: 1100px;
  height: 310px;
  margin: 10px auto 10px auto;
  overflow-x: auto !important;
}

@Millardiang
Copy link

Millardiang commented Jul 28, 2022

Hi,
Thank you for these points.

The first one, I had not actually appreciated that the barbs themselves need to stay in m/s. I have now made your suggested change.

Screenshot 2022-07-28 at 08 48 49

Screenshot 2022-07-28 at 08 49 12

The top one is m/s and the bottom one is mph, so that is fixed thank you.

Regarding the browser issues, I had actually tried overflow-x from your original CSS and the behaviour is similar to what it is with my CSS settings, the barbs shift to the left when the browser is re-sized. I have not worked out yet what else is happening to cause the shift. I have left the code set with overflow-x so that you can see.

Your fix for the icon size difference has solved the effects I had been seeing across different browsers, so thank you for that solution as well.

Ian

@norecords
Copy link
Author

I forgot to tell you something, you have to set the width of the container also

Meteogram.prototype.getChartOptions = function () {
  return {
    chart: {
      renderTo: this.container,
      marginBottom: 70,
      marginRight: 40,
      marginTop: 50,
      plotBorderWidth: 1,
      width: 1100,
      height: 310,
      alignTicks: false
    },

...

combined with the css rules

#meteogram-container {
  min-width: 340px;
  max-width: 1100px;
  height: 310px;
  margin: 10px auto 10px auto;
  overflow-x: auto !important;
}

@Millardiang
Copy link

That has fixed it, big thank you :-)

@dtalens
Copy link

dtalens commented Jul 28, 2022

Ei, today in my meteogram rain numbers appear as shown in this image:

image

Is this normal?

@norecords
Copy link
Author

norecords commented Jul 28, 2022

@dtalens that have been fixed on the last rev https://gist.github.com/norecords/85ea0186497404618574a7c62202f304/revisions#diff-ec09dffc558368608407d7edb446419b262fad1a433a261cc29c60f34783df12

That because of new Highcharts version.
Add that to your css rules:

#meteogram-container .highcharts-text-outline {
  stroke: #ffffff;
}

Also
image
in // Top X axis section
at line 402 change
y: -5
to
y: 5

@norecords
Copy link
Author

norecords commented Aug 1, 2022

@Millardiang @steepleian you can load your Meteocon icon set like that:
replace

      if (Meteogram.dictionary[symbol]) {
        chart.renderer
          .image(
            'https://cdn.jsdelivr.net/gh/nrkno/yr-weather-symbols' +
              `@8.0.1/dist/svg/` + icon + `.svg`,
            point.plotX + chart.plotLeft - 8,
            point.plotY + chart.plotTop - 35,
            30,
            30
          )

by

      if (Meteogram.dictionary[symbol]) {
        chart.renderer
          .image(
            'https://cdn.jsdelivr.net/gh/steepleian/MeteoconsYR@master/' + icon + '.svg',
            point.plotX + chart.plotLeft - 8,
            point.plotY + chart.plotTop - 40,
            35,
            35
          )

that way you use external geo localized cdn from jsdelivr :)

@norecords
Copy link
Author

@dtalens

you forgot to edit this line with your latitude, longitude and altitude

// Edit next line with your latitude, longitude and altitude
const url = 'https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=43.487&lon=6.079&altitude=164';

on your meteogram you use mine...

@dtalens
Copy link

dtalens commented Oct 4, 2022

@dtalens

you forgot to edit this line with your latitude, longitude and altitude

// Edit next line with your latitude, longitude and altitude
const url = 'https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=43.487&lon=6.079&altitude=164';

on your meteogram you use mine...

Uppss thnaks @norecords

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment