-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Thumbnail fv_34 with JSDOM #79
Merged
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
1692bee
merge upstream
c2d12c7
a start. IMPLEMENT
ffd2fed
scrapy stuff here atm, but the svg string export is working. Will nee…
2f6438d
merge upstream
9375729
getting further .. I guess. Tried using svg2png and canvg and neither…
6df16df
moving to use phantomjs
bef9932
Somthing working! Reverted back to jsdom
5ea65ec
use jsdom to pass window to hydromodule thumbnail
8a05655
rivers wasnt worked on. Pesky rivers
1064c9f
hydrograph.html not relevant
a40807a
implementing map thumbnail ability
4ab507d
Merge branch 'master' of https://github.com/USGS-VIZLAB/active-flood-…
b1bee7a
added support for map png
8c20cbb
directory spelling
bf9ea85
directory names in path
bcaa845
moved conversion logic to function
26d0ff9
added argument parser to avoid overlapping png images
9daea30
exit if invalid arguments
b2dc43f
added svg2png to package.json depenecies
62e7091
config var for writing flat files
a14baeb
Merge branch 'master' into thumbnail_FV_34
51456c7
added argument parser and inline style injecttion for external css
0ae52f9
Merge branch 'thumbnail_FV_34' of https://github.com/ede0m/active-flo…
1d76943
removed map code. added error handling for external css bad file path
d1574db
removed some unnecessary code
7e7b94e
spaces
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,3 +22,6 @@ | |
'height': '4', | ||
'width': '7.5' | ||
} | ||
|
||
# Thumbnail Support | ||
THUMBNAIL = False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,5 +32,4 @@ | |
|
||
</body> | ||
|
||
</html> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
|
||
'use strict'; | ||
/** | ||
* @param {Object} options - holds options for the configuration of the hydrograph | ||
* Non-optional Keys include: | ||
* @prop 'height' v(int) - height of the graph | ||
* @prop 'width' v(int) - width of the graph | ||
* @prop 'data' v(list) - A list of objects representing data points | ||
* @prop 'div_id' v(string) - id for the container for this graph | ||
* | ||
* hydromodule is a module for creating hydrographs using d3. Pass it a javascript object | ||
* specifying config options for the graph. Call init() to create the graph. Linked | ||
* interaction functions for other figures should be passed to init in and object. | ||
* | ||
*/ | ||
var hydromodule = function (options) { | ||
|
||
var self = {}; | ||
|
||
var margin = {top: 30, right: 20, bottom: 30, left: 50}; | ||
var height = options.height - margin.top - margin.bottom; | ||
var width = options.width - margin.left - margin.right; | ||
|
||
// Adds the svg canvas | ||
var svg = null; | ||
// Focus for hydrograph hover tooltip | ||
var focus = null; | ||
// Voronoi layer | ||
var voronoi_group = null; | ||
// Define the voronoi | ||
var voronoi = d3.voronoi() | ||
.x(function (d) { | ||
return x(d.time_mili); | ||
}) | ||
.y(function (d) { | ||
return y(d.value); | ||
}) | ||
.extent([[-margin.left, -margin.top], [width + margin.right, height + margin.bottom]]); | ||
// Define the line | ||
var line = d3.line() | ||
.x(function (d) { | ||
return x(d.time_mili); | ||
}) | ||
.y(function (d) { | ||
return y(d.value); | ||
}); | ||
// Set the ranges | ||
var x = d3.scaleTime().range([0, width]); | ||
var y = d3.scaleLog().range([height, 0]); | ||
|
||
/** | ||
* Filters a set of data based on the ids listed in display_ids | ||
* @returns {Array} The entries of the original `data` whose `key` values are elements of display_ids. | ||
*/ | ||
var subset_data = function (full_data) { | ||
var toKeep = []; | ||
full_data.forEach(function (d) { | ||
if (options.display_ids.indexOf(d.key) !== -1) { | ||
toKeep.push(d); | ||
} | ||
}); | ||
return toKeep; | ||
}; | ||
/** | ||
* | ||
* Draws the svg, scales the range of the data, and draws the line for each site | ||
* all based on the data set as it was passed in. Called as needed | ||
* when data changes (as in removal of a line). | ||
* | ||
*/ | ||
var update = function () { | ||
|
||
// Cut the data down to sites we want to display | ||
var sub_data = subset_data(options.data); | ||
// Remove the current version of the graph if one exists | ||
var current_svg = d3.select(options.div_id + ' svg'); | ||
if (current_svg) { | ||
current_svg.remove(); | ||
} | ||
// recreate svg | ||
svg = d3.select(options.div_id) | ||
.append('svg') | ||
.attr('width', width + margin.left + margin.right) | ||
.attr('height', height + margin.top + margin.bottom) | ||
.append('g') | ||
.attr('transform', | ||
'translate(' + margin.left + ',' + margin.top + ')'); | ||
|
||
var graph_data = sub_data.map(function (d) { | ||
return { | ||
'date': d.date, | ||
'key': d.key, | ||
'name': d.name, | ||
'time': d.time, | ||
'time_mili': d.time_mili, | ||
'timezone': d.timezone, | ||
'value': Number(d.value) | ||
}; | ||
}); | ||
|
||
// Scale the range of the data | ||
x.domain(d3.extent(graph_data, function (d) { | ||
return d.time_mili; | ||
})); | ||
y.domain([d3.min(graph_data, function (d) { | ||
return d.value; | ||
}), d3.max(graph_data, function (d) { | ||
return d.value; | ||
})]); | ||
// Nest the entries by site number | ||
var dataNest = d3.nest() | ||
.key(function (d) { | ||
return d.key; | ||
}) | ||
.entries(graph_data); | ||
// Loop through each symbol / key | ||
dataNest.forEach(function (d) { | ||
svg.append('g') | ||
.attr('class', 'hydro-inactive') | ||
.append('path') | ||
.attr('id', 'hydro' + d.key) | ||
.attr('d', line(d.values)); | ||
console.log('Here IN DATANEST'); | ||
}); | ||
// Add the X Axis | ||
svg.append('g') | ||
.attr('class', 'axis') | ||
.attr('transform', 'translate(0,' + height + ')') | ||
.call(d3.axisBottom(x).tickFormat(d3.timeFormat('%B %e'))); | ||
|
||
// Add the Y Axis | ||
svg.append('g') | ||
.attr('class', 'axis') | ||
.call(d3.axisLeft(y).ticks(10, '.0f')); | ||
|
||
// Tooltip | ||
focus = svg.append('g') | ||
.attr('transform', 'translate(-100,-100)') | ||
.attr('class', 'focus'); | ||
focus.append('circle') | ||
.attr('r', 3.5); | ||
|
||
focus.append('text') | ||
.attr('y', -10); | ||
|
||
// Voronoi Layer | ||
voronoi_group = svg.append('g') | ||
.attr('class', 'voronoi'); | ||
voronoi_group.selectAll('path') | ||
.data(voronoi.polygons(d3.merge(dataNest.map(function (d) { | ||
return d.values | ||
})))) | ||
.enter().append('path') | ||
.attr('d', function (d) { | ||
return d ? 'M' + d.join('L') + 'Z' : null; | ||
}) | ||
.on('mouseover', function (d) { | ||
self.linked_interactions.hover_in(d.data.name, d.data.key); | ||
self.activate_line(d.data.key); | ||
self.series_tooltip_show(d); | ||
}) | ||
.on('mouseout', function (d) { | ||
self.linked_interactions.hover_out(); | ||
self.deactivate_line(d.data.key); | ||
self.series_tooltip_remove(d.data.key); | ||
}) | ||
.on('click', function (d) { | ||
self.linked_interactions.click(d.data.key); | ||
self.remove_series(d.data.key); | ||
}); | ||
|
||
}; | ||
|
||
/** | ||
* Initialize the Hydrograph. | ||
* | ||
*@param {Object} linked_interactions - Object holding functions that link to another figure's interactions. | ||
* Pass null if there are no such interactions to link. | ||
* @prop 'hover_in' - linked interaction function for hover_in events on this figure. | ||
* @prop 'hover_out' - linked interaction function for hover_out events on this figure. | ||
* @prop 'click' - linked interaction function for click events on this figure. | ||
* | ||
* | ||
*/ | ||
self.init = function (linked_interactions) { | ||
self.linked_interactions = linked_interactions; | ||
update(); | ||
return self; | ||
}; | ||
|
||
/** | ||
* Returns the svg element node. Primarily used for thumb-nailing. | ||
*/ | ||
self.get_svg_elem = function () { | ||
return d3.select(options.div_id); | ||
}; | ||
/** | ||
* Displays tooltip for hydrograph at a data point in addition to | ||
* corresponding map site tooltip. | ||
*/ | ||
self.series_tooltip_show = function (d) { | ||
focus.attr('transform', 'translate(' + x(d.data.time_mili) + ',' + y(d.data.value) + ')'); | ||
focus.select('text').html(d.data.key + ': ' + d.data.value + ' cfs ' + ' ' + d.data.time + ' ' + d.data.timezone); | ||
}; | ||
|
||
/** | ||
* Removes tooltip view from the hydrograph series | ||
* as well as the correspond mapsite tooltip. | ||
*/ | ||
self.series_tooltip_remove = function (sitekey) { | ||
focus.attr('transform', 'translate(-100,-100)'); | ||
}; | ||
|
||
/** | ||
* Removes a line from the hydrograph. This resizes data | ||
* appropriately and removes accents from the corresponding | ||
* site on the map. | ||
*/ | ||
self.remove_series = function (sitekey) { | ||
var keep_ids = FV.hydrograph_display_ids; | ||
keep_ids.splice(FV.hydrograph_display_ids.indexOf(sitekey), 1); | ||
self.change_lines(keep_ids); | ||
}; | ||
/** | ||
* Update the value of display_ids and call update to redraw the graph to match. | ||
* @param new_display_ids The new set of gages to be displayed. | ||
*/ | ||
self.change_lines = function (new_display_ids) { | ||
FV.hydrograph_display_ids = new_display_ids; | ||
update(); | ||
}; | ||
/** | ||
* Highlight a line. | ||
* @param sitekey the site number of the line to be highlighted | ||
*/ | ||
self.activate_line = function (sitekey) { | ||
d3.select('#hydro' + sitekey).attr('class', 'hydro-active'); | ||
}; | ||
/** | ||
* Un-highlight a line | ||
* @param sitekey the site number of the line to be un-highlighted | ||
*/ | ||
self.deactivate_line = function (sitekey) { | ||
d3.select('#hydro' + sitekey).attr('class', 'hydro-inactive'); | ||
}; | ||
|
||
return self | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/* | ||
* | ||
* This Script is intended to be run after a flask freeze during the build process. | ||
* | ||
* Its main objective is to dynamically create thumbnails for the site figures | ||
* based on the data obtained from our server side flask services. | ||
* | ||
* */ | ||
|
||
|
||
|
||
// Dependency Import | ||
var fs = require('fs'); | ||
var jsdom = require('jsdom/lib/old-api.js'); | ||
var svg2png = require('svg2png'); | ||
// Data imports | ||
var data_hydro = require('../thumbnail/hydrograph_data.json'); | ||
|
||
// Collect script arguments for external css | ||
var style_path = null; | ||
var args = process.argv.splice(process.execArgv.length + 2); | ||
if (args.length > 2) { | ||
console.log('\nUsage: node thumbnail.js ' + | ||
'\n\nOptional flag: -css path/to/css/file.css\n'); | ||
process.exit(); | ||
} else { | ||
if (args[0] === '-css') { | ||
style_path = args[1]; | ||
} else { | ||
console.log('\nUnrecognized argument: ' + args[0] + '\n'); | ||
process.exit(); | ||
} | ||
} | ||
|
||
// Headless Browser Start for DOM | ||
jsdom.env( | ||
|
||
// create DOM hook | ||
"<html><body><div id='hydrograph'></div>" + | ||
"<div id='map'></divid>" + | ||
"</body></html>", | ||
|
||
// load local assets into window environment | ||
[ | ||
'./floodviz/static/bower_components/d3/d3.js', | ||
'./floodviz/static/bower_components/proj4/dist/proj4.js', | ||
'./floodviz/thumbnail/hydro_thumbnail.js' | ||
], | ||
|
||
function (err, window) { | ||
var hydro_figure = window.hydromodule( | ||
{ | ||
'height': 300, | ||
'width': 560, | ||
'div_id': '#hydrograph', | ||
'data': data_hydro, | ||
"display_ids": ['05471200', '05476750', '05411850', '05454220', | ||
'05481950', '05416900', '05464500', '05487470'] | ||
// Refactor Later. I'm assuming this will change with references.json | ||
} | ||
); | ||
convert(hydro_figure,window, 'floodviz/static/css/hydrograph.css', 'floodviz/thumbnail/thumbnail_hydro.png'); | ||
} | ||
); | ||
|
||
// Wrapper around svg2png that injects custom css to inline svg before conversion | ||
function convert(figure, window, css_path, filename) { | ||
var style_ext = null; | ||
var svg_string = null; | ||
var svg = figure.get_svg_elem().node(); | ||
var style_default = fs.readFileSync(css_path, 'utf8'); | ||
figure.init(); | ||
if (style_path !== null) { | ||
try { | ||
style_ext = fs.readFileSync(style_path, 'utf8'); | ||
} catch(error) { | ||
console.log('\nError: external css file path not found.\nUsing only default style.\n\n' + error); | ||
style_ext = null; | ||
} | ||
svg_string = inject_style(style_default, style_ext, svg, window); | ||
} else { | ||
svg_string = inject_style(style_default, null, svg, window); | ||
} | ||
// Takes care of canvas conversion and encodes base64 | ||
svg2png(svg_string) | ||
.then(buffer => fs.writeFile(filename, buffer)) | ||
.then(console.log('\nConverted D3 figure to PNG successfully... \n')) | ||
.catch(e => console.error(e)); | ||
} | ||
|
||
// Hook style to inline svg string. | ||
function inject_style(style_string, ext_style, svgDomElement, window) { | ||
var s = window.document.createElement('style'); | ||
s.setAttribute('type', 'text/css'); | ||
s.innerHTML = "<![CDATA[\n" + style_string + ext_style + "\n]]>"; | ||
var defs = window.document.createElement('defs'); | ||
defs.appendChild(s); | ||
svgDomElement.insertBefore(defs, svgDomElement.firstChild); | ||
return svgDomElement.parentElement.innerHTML; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you start with a brief comment describing what this script does