2014-05-24 22 views
7

FIDDLE < < < < bu soruya daha güncel kod kadar daha vardır yakınlaştırma, gerçek zamanlı veri ile zaman-serisi çizgi grafiği d3.js.kaydırma ve

D3'te gerçek zamanlı (canlı güncelleme) zaman dizisi grafiği oluşturmaya çalışıyorum, bunlar da panlanır (X olarak) ve yakınlaştırılabilir. İdeal olarak, istediğim işlev, çizginin en sağdaki kısmı kullanıcıya görünürse, o zaman grafiğe yeni veriler eklendiğinde, yeni verileri (eksen ölçeklerini değiştirmeden) otomatik olarak yan yana kaydırılır.

My d3.json() şuna benzer JSON diziler dönmelidir istekleri:

[{"timestamp":1399325270,"value":-0.0029460209892230222598710528},{"timestamp":1399325271,"value":-0.0029460209892230222598710528},{"timestamp":1399325279,"value":-0.0029460209892230222598710528},....] 

sayfa ilk yüklendiğinde, Bir istek yapmak için yukarı anda mevcut tüm tarihi almak ve grafik çizin - kolay. Aşağıdaki kod bunu yapar, ayrıca (X'te) kaydırma ve yakınlaştırma sağlar.

var globalData; 
var lastUpdateTime = "0"; 
var dataIntervals = 1; 

var margin = { top: 20, right: 20, bottom: 30, left: 50 }, 
    width = document.getElementById("chartArea").offsetWidth - margin.left - margin.right, 
    height = document.getElementById("chartArea").offsetHeight - margin.top - margin.bottom; 

var x = d3.time.scale() 
    .range([0, width]); 

var y = d3.scale.linear() 
    .range([height, 0]); 

var xAxis = d3.svg.axis() 
    .scale(x) 
    .orient("bottom") 
    .ticks(10) 
    .tickFormat(d3.time.format('%X')) 
    .tickSize(1); 
    //.tickPadding(8); 

var yAxis = d3.svg.axis() 
    .scale(y) 
    .orient("left"); 

var valueline = d3.svg.line() 
    .x(function (d) { return x(d.timestamp); }) 
    .y(function (d) { return y(d.value); }); 

var zoom = d3.behavior.zoom() 
    .x(x) 
    .y(y) 
    .scaleExtent([1, 4]) 
    .on("zoom", zoomed); 

var svg = d3.select("#chartArea") 
    .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 + ")") 
    .call(zoom); 

svg.append("rect") 
    .attr("width", width) 
    .attr("height", height) 
    .attr("class", "plot"); // ???? 

var clip = svg.append("clipPath") 
    .attr("id", "clip") 
    .append("rect") 
    .attr("x", 0) 
    .attr("y", 0) 
    .attr("width", width) 
    .attr("height", height); 

var chartBody = svg.append("g") 
    .attr("clip-path", "url(#clip)"); 

svg.append("g")   // Add the X Axis 
    .attr("class", "x axis") 
    .attr("transform", "translate(0," + height + ")") 
    .call(xAxis); 

svg.append("g")   // Add the Y Axis 
    .attr("class", "y axis") 
    .call(yAxis); 

svg.append("text") 
    .attr("transform", "rotate(-90)") 
    .attr("y", 0 - margin.left) 
    .attr("x", (0 - (height/2))) 
    .attr("dy", "1em") 
    .style("text-anchor", "middle") 
    .text("Return (%)"); 

// plot the original data by retrieving everything from time 0 
d3.json("/performance/benchmark/date/0/interval/" + dataIntervals, function (error, data) { 
    data.forEach(function (d) { 

     lastUpdateTime = String(d.timestamp); // this will be called until the last element and so will have the value of the last element 
     d.timestamp = new Date(d.timestamp); 
     d.value = d.value * 100; 

    }); 

    globalData = data; 

    x.domain(d3.extent(globalData, function (d) { return d.timestamp; })); 
    y.domain(d3.extent(globalData, function (d) { return d.value; })); 

    chartBody.append("path")  // Add the valueline path 
     .datum(globalData) 
     .attr("class", "line") 
     .attr("d", valueline); 

    var inter = setInterval(function() { 
     updateData(); 
    }, 5000); 

}); 

var panMeasure = 0; 
var oldScale = 1; 
function zoomed() { 
    //onsole.log(d3.event); 
    d3.event.translate[1] = 0; 
    svg.select(".x.axis").call(xAxis); 

    if (Math.abs(oldScale - d3.event.scale) > 1e-5) { 
     oldScale = d3.event.scale; 
     svg.select(".y.axis").call(yAxis); 
    } 

    svg.select("path.line").attr("transform", "translate(" + d3.event.translate[0] + ",0)scale(" + d3.event.scale + ", 1)"); 
    panMeasure = d3.event.translate[0]; 

} 

Aşağıdaki kod bloğunda, tüm yeni verileri almak ve bunu grafiğe eklemek için bir http isteği yapıyorum. Bu iyi çalışıyor.

var dx = 0; 
function updateData() { 

    var newData = []; 

     d3.json("/performance/benchmark/date/" + lastUpdateTime + "/interval/" + dataIntervals, function (error, data) { 
      data.forEach(function (d) { 

       lastUpdateTime = String(d.timestamp); // must be called before its converted to Date() 
       d.timestamp = new Date(d.timestamp); 
       d.value = d.value * 100; 

       globalData.push(d); 
       newData.push(d); 

      }); 

      // panMeasure would be some measure of how much the user has panned (ie if the right-most part of the graph is still visible to the user. 
      if (panMeasure <= 0) { // add the new data and pan 

       x1 = newData[0].timestamp; 
       x2 = newData[newData.length - 1].timestamp; 
       dx = dx + (x(x1) - x(x2)); // dx needs to be cummulative 

       d3.select("path") 
        .datum(globalData) 
        .attr("class", "line") 
        .attr("d", valueline(globalData)) 
       .transition() 
        .ease("linear") 
        .attr("transform", "translate(" + String(dx) + ")"); 

      } 

      else { // otherwise - just add the new data 
       d3.select("path") 
        .datum(globalData) 
        .attr("class", "line") 
        .attr("d", valueline(globalData)); 
      } 

      svg.select(".x.axis").call(xAxis); 

     }); 
} 

Ne (bunu ben ne yapmaları gerektiğini sanırım) yapmaya çalışıyorum olsun: Burada gideceğini hayal - Şimdi sadece yeni veriler için tava mantığını çözmek gerekir yeni veri için zaman değerlerinin aralığı (yani, birinci değer ile newData [] dizisinin son değeri arasındaki fark, bunu piksellere dönüştür ve sonra bu sayıyı kullanarak satırı kaydır.

Bu tür iş, ancak sadece ilk güncelleme.Örneğin güncellenen çalışırken fare kullanarak herhangi bir kaydırma/yakınlaştırma yapmak, başka bir sorun ise, çizgi kaybolur ve mutlaka bir sonraki güncelleme üzerinde geri gelmez. potansiyel hatalar hakkında bazı geribildirimler Kodda pot ve/veya bunun nasıl yapılması gerektiği. Teşekkürler.

GÜNCELLEME 1:

Tamam, ben otomatik kaydırma ile sorunun ne olduğunu anladım. Translate vektörünün bazı menşelerden kümülatif bir değere sahip olması gerektiğini fark ettim, bu yüzden dx kümülatif (dx = dx + (x(x2) - x(x1)); yaptıktan sonra, yeni veriler eklendiğinde yana doğru kaydırma çalışmaya başladı.

GÜNCELLEME 2:

Ben şimdi veri alınır ve çizilen bekliyoruz fiili şekilde yakın bir fiddle dahil ettik. Bir dereceye kadar ben hariç istediğimiz şekilde iş gibi görünüyor:

  1. X ekseni onay işaretleri yeni verilerle kaydırmak yok
  2. ben manuel pan yaptığınızda, davranış biraz garip olur Ben yeni veri eklenecek çalıştığında yakınlaştırma/kaydırma ediyorum, ilk pan
  3. (biraz geri atlar) - çizgi kaybolur:
+0

Bu oldukça karmaşık bir sorundur. Keman olmadan, hiç kimsenin size yardım etmeyeceği sanırım. BTW, NVD3'e bir göz attınız mı? Belki de sizin için bir şey var ... – hgoebl

+0

Teşekkürler dostum, evet var ama biraz daha şeyler özelleştirmek ve D3 kullanmayı öğrenmek istiyorum. En azından çözümün bir kısmını almayı umuyorum; Örneğin, yeni verileri arsaya veya çizgiye nasıl ekleyeceğiniz (görünümden bağımsız olarak), vs .. – rex

+1

Seni anlayabiliyorum, ben de aynı durumdayım. D3 harika ama hata ayıklaması zor. Sürüm kontrolü (Git) kullanmak ve bir şey bozulduğunda ilerlerken ve geri sarıldığında tüm değişiklikleri ve küçük adımları gerçekleştirmeniz en iyisidir. Küçük, küçük adımlar ... Ve tüm kopyala yapıştırılmış kodları anlamak önemlidir. Her görselleştirmenin kendi yüzü vardır. – hgoebl

cevap

-1

yerine (konular 'çoklu iş parçacığı' S?) Tüm verileri çizerek ve gereksiz parçaları kırparak, t ihtiyacınız olduğunda her zaman dilediğiniz tüm değerlerin global bir dizisini tutabilirsiniz. o grafiği güncelleyin. Daha sonra x ekseni & değerlerinizi daha kolay yeniden oluşturabilirsiniz. Dezavantajı, kolayca bir geçiş yapamazsınız.

Yani, iki değişken olurdu:

var globalOffset = 0; 
var panOffset = 0; 

globalOffset yeni değerler ve panOffset kaydırmak her zaman doldurmak her zaman güncellenmiş olacaktır. Sonra sadece komplo önce bölümlemek:

var offset = Math.max(0, globalOffset + panOffset); 
var graphData = globalData.slice(offset, offset + 100); 

komple çözüm fiddle bakınız.

+0

hey dostum, cevabınız için teşekkürler ama grafik X'de kaymıyor mu? – rex

+0

Her ikisi de Firefox ve Chrome'la, masaüstüyle ve grafik sol veya sağ tavaları sürükleyerek, elbette veri olduğu sürece sorunlu çalıştı. – Fradenger

+0

Bu benim için Chrome'da gerçekten garip bir şey var, hattın sağ kenarı sağ kenara sabitlendi ve grafik çizgisi kaydırma yerine sola doğru uzandı: / – rex

İlgili konular