【问题标题】:Loading local data for visualization using D3.js使用 D3.js 加载本地数据以进行可视化
【发布时间】:2012-07-09 19:25:36
【问题描述】:

我正在开展一个项目,该项目需要我将一种相当复杂的数据可视化(请参阅this older question)。简而言之,我有大量数据可以导出为 JSON、CSV 或其他任意平面格式,尽管我更愿意尽可能避免使用 XML(有关基础数据的详细说明,请参阅上面的链接问题)。

我已经开始使用 D3 进行可视化,到目前为止,当我使用一些非常简单的数据进行测试时,我编写的布局似乎工作正常,这些数据我在 Javascript 中硬编码为数组。我在 D3 中阅读的有关数据绑定的教程有点令人困惑,因为有些使用 JSON,有些使用 TXT/CSV 格式,而另一些使用硬编码的数组/矩阵。

对于 JSON,我观看了一个教程,其中叙述者强烈建议将 JSON 文件托管在网络服务器上,并使用 HTTP 请求而不是读取本地文件来获取它。我意识到这是由于跨域请求限制,我相信我必须以某种方式解决。在这一点上,我不确定如何进行,因为:

  1. D3 驱动的可视化将在一系列 HTML 报告上,这些报告是作为我编写的分析工具的结果创建的。分析在用户计算机上完成,HTML 报告也在客户端本地创建。

  2. 目标用户绝对不是精通技术的,因此不能指示他们在计算机上运行网络服务器以通过 localhost 提供 JSON 或任何其他类型或资源

作为记录,我已经尝试运行 python SimpleHTTPServer 模块进行尝试,并且一切正常。然后我尝试在生成的 HTML 报告中对数据进行硬编码,然后从我的使用 D3 的脚本中调用 JSON 对象,

//d3.json("mydata.json", function(json){
d3.json(myjson, function(json){
    nodeData = json.elements;
....
}

失败了,因为在这种情况下,我最终发送了一个 JSON 对象,而 D3.js 期待一个 URL。

我可以做些什么来避免/解决这个问题?

【问题讨论】:

  • 我也很想这样做,只是不明白为什么 d3 不接受本地 json 字符串!

标签: javascript json d3.js


【解决方案1】:

所以你想通过 FileReader 调用加载本地数据,下面的内容来自Reading local files in JavaScript。但是,在上面的示例的基础上,此代码将图像文件加载到 svg 中而不链接文件,但实际上将栅格数据插入到 svg 中。并随意用任何其他数据替换栅格数据,只需对其添加正确的处理...

首先在 <body> 部分中支持 html 元素:

<input type="file" id="files" name="files[]" multiple />

然后您想将&lt;input ... /&gt; 元素链接到&lt;script&gt; 部分中的某些代码:

document.getElementById('files').addEventListener('change', handleFileSelect, false);

现在在事件处理代码中读取文件背后的代码(现在在这种情况下,我想将本地图像加载到 D3JS svg 中):

function handleFileSelect(evt) {
    reader = new FileReader();
    reader.onabort = function(e) {alert('File read cancelled');};
    var imageObj = new Image();  //image object to hold the local file contents.

    //and given the asynchronous nature, set up the event so that when the image is loaded, do something with it:
    imageObj.onload = function() {image.datum(imageObj.src).attr("xlink:href", function(d) {return d})};

    //similarly for the file reading:
    reader.onload = function(e) {imageObj.src = reader.result};

    //With all the events set up, lets start reading the file in.
    //the secret sauce is the DataURL
    reader.readAsDataURL(evt.target.files[0]);
}

为了完整起见,图片背后的 D3JS 酱汁:

var svg = d3.select("body").append("svg");

var image = svg.append("defs")
    .append("pattern")
    .attr("id", "venus")
    .attr('patternUnits', 'userSpaceOnUse')
    .attr("width", 200)
    .attr("height", 200)
    .append("image")
    .attr("width", 200)
    .attr("height", 200);

var image2 = svg.append("rect")
    .attr("x", "0")
    .attr("y", "0")
    .attr("width", 200)
    .attr("height", 200)
    .attr("fill", "url(#venus)");

【讨论】:

    【解决方案2】:

    对于 JSON:

    var data = JSON.parse(JavascriptStringVariableThatContainsJSON);
    

    对于 CSV:

     var data = d3.csv.parseRows(JavascriptStringVariableThatContainsMyCSVdata);
    

    //然后将数据添加到图表并调用回车,类似于:

     var dataEnter = svg.selectAll("rect").data(data).enter();
    

    【讨论】:

      【解决方案3】:

      d3.json 通过 ajax 调用加载外部 json 文件 - 您示例中的 myjson 变量已经是一个 javascript 对象,因此您无需加载它,只需在您的 nodeData 分配中直接使用它即可。

      nodeData = myjson.elements;
      

      【讨论】:

        【解决方案4】:

        要在不使用服务器的情况下加载数据本地文件,请将脚本标记附加到正文,并将源数据分配给您期望的变量或对象元素。在下面的示例中,我从 [COUNTRY_ISO3CODE].js 文件加载某个国家/地区的数据。具体来说,阿富汗数据文件可能包含以下格式的内容:

        data[AFG] = {"name": "Afghanistan", "estimate": 9.003, ... }
        

        调用文件将具有以下内容:

        if (!data[iso3]) { //only load the needed data-script as needed
        
            // if using jQuery
            $('body').append("<script type='text/javascript' src='jdb/"+ iso3 +".js'></script>")
        
            //If not using jQuery
            /*var script   = document.createElement("script");
            script.type  = "text/javascript";
            script.src   = "jdb/"+iso3+".js"; //?_="+ (new Date()); URL might have to be randomized to avoid cached data
            document.body.appendChild(script);
            */
        
            // call the data-handling function only after 
            // the desired data has fully loaded
            // in this case, I check every 100 milliseconds
            var intervalId = setInterval( function () {
                if (data[iso3]) {    //once data is detected, process data
                    clearInterval(intervalId); //stop checking for data
                    prepData(mainWrapper, iso3)
                    drawCharts(mainWrapper, iso3)
                }
            }, 100)
        }
        else drawCharts(mainWrapper, iso3)
        

        【讨论】:

        • 前面的答案是动态加载数据。对于静态数据加载,只需像加载 d3.js 或其他 javascript 文件一样,直接在 html 文件中写入 script 标签。
        【解决方案5】:

        如果您每次都生成 HTML,您可以将数据作为 JSON 放入您的 HTML 或您可以从 HTML 引用的 .js 文件中,可能通过生成的唯一 URL。

        【讨论】:

        • HTML 是在客户端(可能)离线创建的,而不是在我们的服务器上。还能适用吗?
        【解决方案6】:

        D3 有一些很棒的工具可以用来导入和操作数据。过去一直提供 json 服务(有时创建起来很麻烦)我更倾向于提供 csv 并在 javascript/d3 中操作它,但有兴趣看看其他人如何回答您的问题。

        这是一个简单的例子,在这里展示了 csv 到嵌套的 json:https://gist.github.com/3053667

        还有很多处理数据的工具:https://github.com/mbostock/d3/wiki/Arrays

        如果你想切片和切块,还有交叉过滤器。看起来很值得掌握,但我正在等待假人指南出来! http://square.github.com/crossfilter/

        【讨论】:

        • 但本质问题是 CSV 和 JSON 都需要来自 HTTP 请求,而不是来自本地文件,对吧?
        • 根据我的经验,您将不得不让客户端以某种方式“加载”文件。您可以使用文件输入表单或将数据复制并粘贴到文本字段中或使用 ftp 问题将数据放在服务器上 - 文件或数据库中。否则,您能否在客户端上托管,也许使用 node.js?
        • 你能详细说明一下“加载”的想法吗?考虑到我的大多数目标用户远非精通技术,我想让它尽可能简单和自动化。
        猜你喜欢
        • 2013-03-03
        • 1970-01-01
        • 2016-09-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多