Is it possible to retrieve data from an svg via d3?
I have following scenario:
on page Resize I need to update the widths of an svg which was generated on a server via d3 . So for example the x-axis. However the client side d3 library has no knowledge of the svg. I noticed that with each DOM object there is a __ chart__ object. Is there some way i can access the range and domain for example and update them accordingly?
When you create an SVG on the server and transfer it to the client, only the actual SVG DOM gets transferred, not any javascript objects or properties (such as the d3 __data__ property) that you used in creating it.
So in order to attach data to the SVG elements that actually gets passed with the file, you would need to create an attribute of that element that contains the data. Then you can select the element client side and parse the data attribute.
Example:
/*** Server Side ***/
/* if `bars` is a d3 selection of rectangles in a bar graph */
bars.attr("data-name", function(d){return d.name;})
.attr("data-value", function(d) {return d.value;});
/*** Client Side ***/
var bars = d3.selectAll("rect.bar")
.datum(function(){
return {
name: this.getAttribute("data-name"),
value: this.getAttribute("data-value")
}
})
.on("click", function(d,i){
/* do something with the data */
});
That works if the data in question is simply a few numbers or names that you want to use in tooltips or similar interaction. It doesn't work for a complex object like a chart function. If you're need to redraw/resize the chart client-side, I really don't see any performance benefit in trying to draw the graphs server-side.
You can create server scripts to parse all your data into a ready-to-use format, maybe even run the d3 layout functions to create a ready-to-draw JSON array. But then draw the graph client-side.
Related
my code:
function d3_chart() {
// sample data array
// instantiate d3plus
var visualization = d3plus.viz()
.container("#viz") // container DIV to hold the visualization
.data("./extra/acc.csv", {"filetype": "csv"}) // data to use with the visualization
.type("line") // visualization type
.y("x") // key to use for y-axis
.x("timestamp") // key to use for x-axis
.draw() // finally, draw the visualization!
}
my csv:
timestamp,x,y,z
0,2019-02-28 12:20:19.631,1.072,-0.153,10.113
1,2019-02-28 12:20:19.731,1.072,-0.153,10.419
2,2019-02-28 12:20:19.831,1.072,-0.153,9.96
3,2019-02-28 12:20:19.931,1.072,-0.153,10.113
4,2019-02-28 12:20:20.031,1.072,-0.153,10.113
5,2019-02-28 12:20:20.132,1.225,-0.153,9.96
6,2019-02-28 12:20:20.231,1.225,-0.153,9.96
7,2019-02-28 12:20:20.331,1.225,-0.153,9.96
8,2019-02-28 12:20:20.431,0.919,-0.306,9.5
9,2019-02-28 12:20:20.531,0.919,0.459,9.807
10,2019-02-28 12:20:20.631,1.225,0.153,10.113
11,2019-02-28 12:20:20.731,1.379,-1.992,10.113
12,2019-02-28 12:20:20.831,1.838,-0.306,9.653
13,2019-02-28 12:20:20.931,0.153,0.766,10.113
14,2019-02-28 12:20:21.032,0.459,1.532,10.266
15,2019-02-28 12:20:21.133,1.072,0.0,9.96
I just got getting message:
No Data Available
What is wrong? I don't find any example in internet with csv loading via this library
Or something know how graph chart from csv via general D3 with simple example?
d3plus seems to be using v3.5.15 of d3.js. Regardless of that, you will need to tell d3plus of how to load the data. Reading the API documentation it seems you will have to load the data using
d3plus.dataLoad(path, [formatter], [key], [callback]) as explained here.
Alternatively, you can use d3.js to parse your csv file and pass it as the data. To do this you can use the
d3.csv.parse(string[, accessor]) as provided in the d3.js CSV API.
Keep in mind in both cases you will need to format your timestamps in the correct time format (For d3.js Time Format API doc), for you to be able to use the time scales. Also at least for d3.js when the data is parsed from CSV all values are string values and hence you will need to change the type of the values using an type conversion function. You can read more about this in a great guide on how to read data by Learnjsdata (d3.js v3, or d3.js v5)
There are several examples out there for d3.js v3 on importing the data for processing which may be a better option overall. Also consider d3plus has not got a github commit in over a year so the library may not be well supported.
I hope this helps and at least gives you a start. If you need more help please leave a comment below.
I am having a problem with loading data into d3.js. I have populated a data.json file with the following:
"[[11,53,32.0,20],[4,57,160.0,20],[4,58,192.0,20],[2,63,352.0,20],[0,66,448.0,20],[0,67,480.0,20],[0,67,480.0,170],[0,67,480.0,320],[0,68,512.0,20],[0,69,544.0,20],[4,70,576.0,20],[0,70,576.0,170],[4,70,576.0,320],[0,72,640.0,20],[0,73,672.0,20],[0,75,736.0,20],[4,75,736.0,170],[0,76,768.0,20],[0,78,832.0,20],[0,79,864.0,20],[0,81,928.0,20]]"
I want to load this data into JSON and then construct some objects using it. This is the responsible d3 bit:
frame.select("g").data(dataset).enter().append("img")
.attr("src", "Shuttle Booster Diagram Simplified.svg")
.attr("height", "150px")
.style("background-color", "red");
However instead of loading the 20 rocket diagrams that I was expecting, this loaded some 300-plus of them!
If I knock the dataset down to
"[[11,53,32.0,20]"
It loads 15 copies.
What is my mistake? Should I populate the data some other way in order to avoid this issue?
The repo for this effort:
https://github.com/ResidentMario/visualizations/tree/master/shuttle
In Google visualization ChartWrapper class, the code to draw a chart (bar chart, pie chart, etc) is as follows:
wrapper = new google.visualization.ChartWrapper({
dataTable: dataTable.toJSON(),
chartType: chartType,
options: options
});
console.log("Wrapper: " + wrapper.toJSON());
console.log(" ");
wrapper.draw(result[0]);
I am curious if there's an equivalent way of doing this using Highcharts where instead of calling new google.visualization.chartWrapper(...), we can just call something like new highcharts.chartWrapper(...). The options can include the container div id where the chart will be drawn.
Even if there is no way to do, any suggestions how I should be go about doing something similar to this?
As far as I know, in dataTable I can specify my data series in JSON format. In chartType, I can specify bar, column, pie, etc. Based on the highcharts example I saw, I can even write a service that generates a HTML file on the server side as shown here -> http://jsfiddle.net/gh/get/jquery/1.9.1/highslide-software/highcharts.com/tree/master/samples/highcharts/demo/column-basic/ and send back a response.
But what I don't know is how do I tell Highcharts to insert the chart right at container id specified in my options on the same page.
In this example, for instance, wrapper is just a json representation of the chart (which I can generate). What I am not sure is how do I convert that json representation to a highcharts map that gets inserted where I want on the same page.
Sorry, if this question seems complicated. I can clarify if you need additional information.
I am trying to transition between two GeoJSON objects, but have been unable to find any information on how to include more than one GeoJSON file. I am happy with the final transformation, but cannot find anything on multiple GeoJSON files.
I can currently plot one map like so:
d3.json("goldatt2.json", function (data) {
var group = canvas.selectAll("g")
.data(data.features)
.enter()
.append("g")
var path = d3.geo.path().projection(d3.geo.equirectangular());
var areas = group.append("path")
.attr("d", path)
How would I change this so that two GeoJSON files are called and can be transformed?
I found this example, http://bl.ocks.org/mbostock/3081153 but in this example there is only GeoJSON object that is then transformed to a circle.
Getting the Data is separate task with respect to transforming it.
So you could, in theory, have multiple ajax calls to fetch the data you need before you decide to do whatever with that data.
Mike Bostock has a Queue implementation for this purpose.
If you are using JQuery with D3, you could look into Deferred an example of which is posted here.
Once you have the data-sets that you need, you can now decide how you want to transform the data or provide transitions between them.
Pseudo-code:
var files = ["file1.json", "file2.json"];
//Fetch Data for both files and create a data set object of this format
var dataset = {
"file1.json": {/*geojson data for file1*/},
"file2.json": {/*geojson data for file2*/}
}
for(var item in dataset) {
var data = dataset[item];
//Do whatever transformation you need
}
I'm trying to draw US state outlines with the D3 framework (http://mbostock.github.com/d3/) but am having issues generating the actual SVG data. I've written my code to follow the Chloropleth example (as it most closely resembles what this project needs), made sure the supplied data is in geoJSON format, and AFAIK have the backend half of this working fine.
The problem is that when I view the DOM, the <svg> object contains only one <g> element (which I created manually, per the example), and none of the child <path> elements that should under it. My code seems fairly identical to the example, and my data appears to look correct, though I am outputting MultiPolygons instead of the Polygon object that the example uses.
Our app is a RoR project with jQuery (we're only using D3 for the SVG and geography features). The test page tries to create an <svg> element under a div called theArea, based upon the selection from a dropdown select of U.S. states:
$(document).ready( function() {
$("#chooser_state").change( function() {
var status = "#status";
var statebox = "#chooser_state";
var theArea = "#theArea"
var url = "/test/get_state_geom";
var data = { state: $(statebox).val() };
$(status).text("Request sent...");
$.post(url, jQuery.param(data), function(resp) {
$(status).text("Received response: " + resp["message"]);
$(theArea).empty();
var path = d3.geo.path();
var svg = d3.select(theArea).append("svg");
var state = svg.append("g").attr("id", "state_view");
var features = resp.payload.features;
$(status).text("Created SVG object");
state.selectAll("path")
.data(features)
.enter()
.append("path")
.attr("d", path );
});
});
});
The data we're feeding D3 looks like this:
{
'type' => 'Feature',
'id' => '01',
'properties' => {
'name' => 'Colorado'
},
'geometry' => {
'type' => 'MultiPolygon',
'coordinates' => [
[
[
[
-106.190553863626,
40.9976070173843
],
[
-106.061181,
40.996998999999995
],
< -- and so on -- >
]
]
]
}
}
Can someone clue me in to what we're doing wrong? I am new to geo and GIS stuff. I suspect the problem lies with the data() function, as it looks like it should be creating the blank <path> objects for each Feature (though we have only one, at the moment), but the D3 documentation seems unclear (and difficult to understand).
EDIT: Just wanted to add that the geoJSON we generate was created by the geoJSON extension for the GeoRuby gem. The actual map lines were sourced from the consolidated data that US Census Bureau's cartographic boundary files, which were converted to SQL and saved with postGIS. Part of me suspects the geoJSON extension is doing something wrong, so that is my next avenue of attack.
After giving up on this and then coming back, I noticed that my FeaturesCollection was not, in fact, a collection. There's a small detail that is easy to overlook when examining geoJSON samples: the contents of the FeaturesCollection is an array of hashes, not a single hash.