Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
James Taylor wanted to load CSV data and display it as a gauge data visualization in WordPress. See https://www.facebook.com/groups/advancedwp/posts/4673874846008026/
<?php
/**
* Plugin Name: River Height Levels
* Plugin URI: https://gist.github.com/kingkool68/567527d28f82bd3a0107970b624fea0a
* Description: Fetch the current river height values and display it as different data visualization.
* Version: 0.0.2
* Author: Russell Heimlich
* Author URI: https://twitter.com/kingkool68
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
class River_Height_Levels {
/**
* Get an instance of this class
*/
public static function get_instance() {
static $instance = null;
if ( null === $instance ) {
$instance = new static();
$instance->setup_actions();
}
return $instance;
}
/**
* Hook into WordPress via actions
*/
public function setup_actions() {
add_action( 'init', array( $this, 'action_init' ) );
}
/**
* Register the Google Charts loader script
*/
public static function action_init() {
wp_register_script(
'google-charts-library',
'https://www.gstatic.com/charts/loader.js',
$deps = array(),
$ver = null,
$in_footer = true
);
// Register dummy script handlers so we can dynamically enqueue wp_add_inline_script()
// See https://wordpress.stackexchange.com/a/311279/2744
wp_register_script(
'river-gauge-chart',
'',
$deps = array( 'google-charts-library' ),
$ver = null,
$in_footer = true
);
wp_register_script(
'river-line-chart',
'',
$deps = array( 'google-charts-library' ),
$ver = null,
$in_footer = true
);
add_shortcode(
'river-height-gauge',
function() {
wp_enqueue_script( 'river-gauge-chart' );
$the_javascript_goodness = static::render_gauge_chart_javascript();
wp_add_inline_script( 'google-charts-library', $the_javascript_goodness, $position = 'after' );
return '<div id="river-gauge-chart" style="width: 400px; height: 200px;"></div>';
}
);
add_shortcode(
'river-level-chart',
function() {
wp_enqueue_script( 'river-line-chart' );
$the_javascript_goodness = static::render_line_chart_javascript();
wp_add_inline_script( 'google-charts-library', $the_javascript_goodness, $position = 'after' );
return '<div id="river-line-chart" style="width: 900px; height: 500px"></div>';
}
);
}
/**
* Load an external CSV file and store it as a transient
*/
public static function get_the_csv_data() {
$transient_key = 'river-height-csv-data';
$data = get_transient( $transient_key );
// If the data already exists then return it. We're done here!
if ( false !== $data ) {
return $data;
}
// Fetch the CSV data.
$request = wp_remote_get( 'https://check-for-flooding.service.gov.uk/station-csv/8056' );
// Retrieve the body contents from the request.
$raw_csv_string = wp_remote_retrieve_body( $request );
$rows = explode( "\n", $raw_csv_string );
// Remove the header rows from the beginning of the array.
array_shift( $rows );
$data = array();
foreach ( $rows as $row ) {
$data[] = str_getcsv( $row, ',' );
}
// Store the result for 15 minutes.
set_transient( $transient_key, $data, 15 * MINUTE_IN_SECONDS );
return $data;
}
/**
* Render the latest data point as a Google Visualization Gauge Chart
*
* @link https://developers.google.com/chart/interactive/docs/gallery/gauge
*/
public static function render_gauge_chart_javascript() {
$csv_data = static::get_the_csv_data();
$last_row = end( $csv_data );
ob_start();
?>
google.charts.load('current', {'packages':['gauge']});
google.charts.setOnLoadCallback(drawGaugeChart);
function drawGaugeChart() {
var data = google.visualization.arrayToDataTable([
['Label', 'Value'],
['Height', <?php echo floatVal( $last_row[1] ); ?>]
]);
var options = {
min: 0,
max: 2,
width: 400,
height: 200,
redFrom: 1.2,
redTo: 2,
greenFrom: 0.4,
greenTo: 1.2,
yellowFrom: 0,
yellowTo: 0.4,
minorTicks: 5
};
var chart = new google.visualization.Gauge(document.getElementById('river-gauge-chart'));
chart.draw(data, options);
}
<?php
return ob_get_clean();
}
/**
* Render the river height data point as a Google Visualization Line Chart
*
* @link https://developers.google.com/chart/interactive/docs/gallery/linechart
*/
public static function render_line_chart_javascript() {
$csv_data = static::get_the_csv_data();
ob_start();
?>
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawLineChart);
function drawLineChart() {
var data = google.visualization.arrayToDataTable([
['Date', 'Height'],
<?php
foreach ( $csv_data as $row ) {
$the_date = date( 'g:i a D, M j', strtotime( $row[0] ) );
echo ( "['" . $the_date . "', " . floatVal( $row[1] ) . '],' );
}
?>
]);
var options = {
title: 'River Level Over Last 5 Days',
curveType: 'function',
hAxis: {
showTextEvery: 96,
format: 'D M j'
},
vAxis: { minValue: 0, maxValue: 2 },
legend: { position: 'bottom' }
};
var chart = new google.visualization.LineChart(document.getElementById('river-line-chart'));
chart.draw(data, options);
}
<?php
return ob_get_clean();
}
}
River_Height_Levels::get_instance();
@James--Taylor

This comment has been minimized.

Copy link

@James--Taylor James--Taylor commented Oct 20, 2021

Hi Russell, having spent some time going through the code to try and understand it / get it working in my scenario but I still seem to be struggling!

Firstly, I am using Oxygen Builder in-conjunction with WordPress to create the site, as such I believe I need to use your code in the form of a plugin?! To try and make progress I have been using your code provided with the ‘My Custom Function’ plugin to test but I’m sure it’s not the cleanest way to be heading.

The CSV data URL I’m working with is: https://check-for-flooding.service.gov.uk/station-csv/8056 (it is river level data from the UK government agency)

Under the drawChart function my information would be:

[‘Label’, ‘Value’],
[‘Height’, ‘ $currentHeight’]
– where $currentHeight is the most current timestamp height reading in the CSV download data.

And the options for the gauge would be:

var options = {
min: 0,
max: 2,
width: 400,
height: 200,
redFrom: 1.2,
redTo: 2,
greenFrom: 0.4,
greenTo: 1.2,
yellowFrom: 0,
yellowTo: 0.4,
minorTicks: 5
};

I am also hoping to incorporate a 2nd Google Chart showing the all the river data from the CSV as well (I was initially just trying to get the gauge example working) – the 2nd chart would be as seen here - https://check-for-flooding.service.gov.uk/station/8056

For context the gauge and the chart are being shown on my Kayaking club’s website so members / paddlers have an accessible indicator of both the current river level and also the 5 day history in order to illustrator whether the level is raising or falling etc.

If you are able to offer any further help it would be very much appreciated!

Many thanks, Regards James

@kingkool68

This comment has been minimized.

Copy link
Owner Author

@kingkool68 kingkool68 commented Oct 20, 2021

@James--Taylor Ah the extra details help. I have updated the gist so it can be a plugin. You can just drop the file into your /wp-content/plugins/ directory, activate it, and then add a shortcode of where you want the gauge to show up. Example: [river-height-gauge]. It should look like this:

Screen Shot 2021-10-20 at 10 38 54 AM

You'll see above the gauge is the actual data I managed to grab from the CSV file.

My Dad is into kayaking. He's got about 3 or 4 boars in the garage and he goes out sea kayaking all the time.

@James--Taylor

This comment has been minimized.

Copy link

@James--Taylor James--Taylor commented Oct 20, 2021

@kingkool68 thank you so much! I've been plugging away this afternoon trying to make some headway by stripping everything back to basics and running it on a standard WordPress theme (no oxygen builder) just to see if I could get what I needed working... I was making slow progress! But your code / plugin has worked straight off. Thank you!

I will now try to get the full 5 day chart working following the same principles you have provided and see if I can figure translating the time stamp into a more friendly display!

Your dad sounds like most of us at our club, a selection of boats so we have something to choose for every occasion! Do you ever join him?

Thank you once again.

@alanef

This comment has been minimized.

Copy link

@alanef alanef commented Oct 20, 2021

Check for error on response from remote get or you will save bad data if there is a network glitch

e.g.

if ( is_wp_error( $request ) ) {
	//  do error stuff
}
$response_code = wp_remote_retrieve_response_code( $request );
if ( 200 !== $response_code ) {
	// do error stuff
}
@James--Taylor

This comment has been minimized.

Copy link

@James--Taylor James--Taylor commented Oct 22, 2021

@kingkool68

Hi Russell, I hope you don't me commenting again... I have been trying to implement the 2nd chart I wanted to display with some success and some failure! - I have been able to get both the gauge and the chart working and both displaying on page in a test 'raw WordPress' (No Oxygen Builder) environment - file myTest.php

I have then attempted to translate my working line graph element, showing all the 5 day data, into a plugin by duplicating and editing your original example above to a new plugin to test (I was then going to try and combine both into a single plugin). - file riverlevelchart.php

However... when I install the duplicated plugin for the chart and display the page I get the following error:

Data column(s) for axis #0 cannot be of type string

I am assuming that for some reason my data is not populating the table under: var data = google.visualization.arrayToDataTable when implemented as a plugin (as it is working in when 'raw') - but I have no idea why!

Could I trouble you once again for some advice as I'm now stumped and my brain is overheating!...

Both my files are in a zip on the following link - (https://www.dropbox.com/s/sxout7aefatfx79/files.zip?dl=0 )

Regards James

@kingkool68

This comment has been minimized.

Copy link
Owner Author

@kingkool68 kingkool68 commented Oct 22, 2021

@James--Taylor

I spotted the problem. In the action_wp_enqueue_scripts method you had this:

foreach ( $data as $row ) {
	echo ( '[\'' . date( 'g:i a D, M j', strtotime( $row[0] ) ) . '\', ' . $row[1] . '],' );
}

In the first line of the method we have this $csv_data = static::get_the_csv_data();. You were trying to loop over $data, which doesn't exist, instead of $csv_data. The fixed version should look like this:

foreach ( $csv_data as $row ) {
	echo ( '[\'' . date( 'g:i a D, M j', strtotime( $row[0] ) ) . '\', ' . $row[1] . '],' );
}
@kingkool68

This comment has been minimized.

Copy link
Owner Author

@kingkool68 kingkool68 commented Oct 22, 2021

Let me see if I can update this gist to include both charts in one simple plugin.

@James--Taylor

This comment has been minimized.

Copy link

@James--Taylor James--Taylor commented Oct 22, 2021

@kingkool68

Aaah... I see!... because the action_wp_enqueue_scripts is storing the results of get_the_csv_data() to $csv_data the data I'm trying to work with is in effect 'moved' from the variable $data to $csv_data

I was closer than I thought, but I thought my problem was in the processing of the requested CSV when fetching the data! I've learned a little bit more once again! Thank you so much!

@James--Taylor

This comment has been minimized.

Copy link

@James--Taylor James--Taylor commented Oct 22, 2021

If it is possible to update the gist to include both charts (called by separate shortcodes) that would be amazing. I will then be able to compare and contrast the two separate plugin files and the consolidated one to try and understand the differences!

Your help has been and is very much appreciated.

@kingkool68

This comment has been minimized.

Copy link
Owner Author

@kingkool68 kingkool68 commented Oct 22, 2021

I just updated the gist. There are now two shortcodes: [river-height-gauge] and [river-level-chart]. They can be used separately or together or however you see fit.

They should look like this:

Screen Shot 2021-10-22 at 9 58 08 AM

This is what I threw into the Gutenberg editor:

<!-- wp:paragraph -->
<p>River Level Chart</p>
<!-- /wp:paragraph -->

<!-- wp:shortcode -->
[river-level-chart]
<!-- /wp:shortcode -->

<!-- wp:paragraph -->
<p>Guage Chart</p>
<!-- /wp:paragraph -->

<!-- wp:shortcode -->
[river-height-gauge]
<!-- /wp:shortcode -->

Hopefully that does what you want. Best of luck!

@James--Taylor

This comment has been minimized.

Copy link

@James--Taylor James--Taylor commented Oct 22, 2021

@kingkool68 Thank you... I'm so sorry, I have installed the updated code as a plugin and I'm not able to get any output on screen with the shortcodes either when only one is tried or when both are used?

I have also installed on a separate site and tried both with no other plugins activated and via the Gutenberg editor but neither display?

I am getting the div's outputted on the front end from the add_shortcode code sections but when I look in the WP database there doesn't seem to be any transient data being created / stored?

@James--Taylor

This comment has been minimized.

Copy link

@James--Taylor James--Taylor commented Oct 26, 2021

@kingkool68

Hi Russell, I have finally managed to get this working!... however I have had to change the code on line 87 - 95 to be:

if ( !wp_script_is( 'river-gauge-chart', $list = 'enqueued' ) ) { $the_javascript_goodness = static::render_gauge_chart_javascript(); wp_add_inline_script( 'google-charts-library', $the_javascript_goodness, $position = 'after' ); }

if ( !wp_script_is( 'river-line-chart', $list = 'enqueued' ) ) { $the_javascript_goodness = static::render_line_chart_javascript(); wp_add_inline_script( 'google-charts-library', $the_javascript_goodness, $position = 'after' ); }

Which is obviously incorrect in terms of the logic that should be getting applied?!

@kingkool68

This comment has been minimized.

Copy link
Owner Author

@kingkool68 kingkool68 commented Oct 26, 2021

@James--Taylor Ah yea that would do the opposite of what we want it to do. I copied the code of the gist and tested it locally as a plugin again. It worked as intended. In the action_wp_enqueue_scripts method you can add debugigng statements like var_dump( 'BEEP' ); and see if that method is actually being called at all.

When you try and use the shortcodes what is the output? Is it just [river-height-gauge] which would mean the shortcode isn't being registered. Or can you see the HTML <div id="river-gauge-chart" style="width: 400px; height: 200px;"></div> in the source code of the page?

@James--Taylor

This comment has been minimized.

Copy link

@James--Taylor James--Taylor commented Oct 26, 2021

@kingkool68 When I run your plugin code from the Gist I get the <div id="river-gauge-chart" style="width: 400px; height: 200px;"></div> being output to the HTML but no further population of content within it.

I also worked out that I was not getting any transient data written to the DB, and additional Javascript components of the Google Visualisation charts where not being loaded / shown in my HTML page source from the google.charts.load('current', {'packages':['corechart']}); google.charts.setOnLoadCallback(drawLineChart);

If I'm reading the plugin code and its data flow correctly, the 'guts' of the plugin ( get_the_csv_data() / render_gauge_chart_javascript() / render_line_chart_javascript() ) are never being called as the checks for wp_script_is()... aren't returning TRUE ??

When I add var_dump( 'BEEP' ); within the action_wp_enqueue_scripts I don't get any output of BEEP unless I change it to !wp_script_is ??!

@kingkool68

This comment has been minimized.

Copy link
Owner Author

@kingkool68 kingkool68 commented Oct 26, 2021

@James--Taylor Ahh I see the issue. I just updated the Gist again. got rid of the whole action_wp_enqueue_scripts() part and simplified it. Should work as expected now.

@James--Taylor

This comment has been minimized.

Copy link

@James--Taylor James--Taylor commented Oct 26, 2021

@kingkool68 Thanks Russell, I have updated and it works!

Thank you once again!

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