【问题标题】:Create bode plot with minor and major ticks d3js使用次要和主要刻度 d3js 创建波特图
【发布时间】:2015-12-23 10:07:46
【问题描述】:

我正在尝试使用 d3js 创建一个典型的波特图。使用 Matlab 创建的典型波特图如下所示:

现在我已经走了很远,请看:http://plnkr.co/edit/BpWis5uhC8KM2tRbXMk3 或本文中嵌入的 sn-p。

我遇到的问题是我想要,就像在 matlab 波特图中一样,有主要和次要滴答声。主要刻度是实心的,而次要刻度是点状的。不幸的是,我似乎无法做到这一点。我已经尝试了各种解决方案:

Major and minor ticks with different style, whole page covered D3? Major and minor ticks with V3 of D3? d3.js alternative to axis.tickSubdivide? http://bl.ocks.org/vjpgo/4689130 http://bl.ocks.org/mbostock/4349486

<!DOCTYPE html>
<html lang="en">
<head>
<title>LOG</title>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//mathjs.org/js/lib/math.js"></script>
<style type="text/css">
svg {
    font: 10px sans-serif;
    shape-rendering: crispEdges;
}
  
rect {
    fill: transparent;
}
  
.axis path,
.axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}
  
.line {
    fill: none;
    stroke: steelblue;
    stroke-width: 1.5px;
    clip-path: url(#clip);
}
  
.grid .tick {
    stroke: lightgrey;
    opacity: 0.7;
}
  
.grid path {
    stroke-width: 0;
}
</style>
<script type="text/javascript">
	function linspace(a,b,n) {
		var every = (b-a)/(n-1),
			range = [];
		for (i = a; i < b; i += every)
			range.push(i);
		return range.length == n ? range : range.concat(b);
	}
	
	function logspace(a,b,n) {
		return linspace(a,b,n).map(function(x) { return Math.pow(10,x); });
	}
	
	function isInteger(value) {
		return typeof value === "number" && 
			   isFinite(value) && 
			   Math.floor(value) === value;
	};
	
	function leadlag(f) {
		w = 2*math.pi*f;
		s = math.complex(0,w);

		K  = 1;
		fz = 20;
		fp = 40;

		wz = 2*math.pi*fz;
		wp = 2*math.pi*fp;

		return math.multiply(K,math.multiply(math.divide(wp,wz),math.divide(math.add(s,wz),(math.add(s,wp)))));
	};
	
	function angle(f) {
		return math.atan2(f.im,f.re);
	};
	
	function deg2rad(deg) {
  		return deg * math.pi / 180;
	};
 
	function rad2deg(rad) {
  		return rad * 180 / math.pi;
	};
	
	function mag2db(mag) {
		return 20 * Math.log10(mag);
	}
	
	function db2mag(db) {
		return math.pow(10,db / 20);
	}
</script>
</head>
<body>
<div id="plotmagnitude"></div>
<div id="plotphase"></div>
<script type="text/javascript">
	var margin = {
        top: 20,
        right: 20,
        bottom: 35,
        left: 50
    };
	
	var width  = 450 - margin.left - margin.right;
	var height = 250 - margin.top - margin.bottom;
	
	var range = logspace(0,3,1000);

    var x = d3.scale.log()
		.domain([1, range[range.length-1].toFixed()])
		.range([0, width]);

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

    var xAxis1 = d3.svg.axis()
		.scale(x)
		.orient("bottom")
		.ticks(1,"0.1s")
		.innerTickSize(-6)
		.outerTickSize(0)
		.tickPadding(7);

    var yAxis1 = d3.svg.axis()
		.scale(y)
		.orient("left")
		.ticks(5)
		.innerTickSize(-6)
		.outerTickSize(0)
		.tickPadding(7);

    var xAxis2 = d3.svg.axis()
		.scale(x)
		.orient("top")
		.ticks(5)
		.innerTickSize(-6)
		.tickPadding(-20)
		.outerTickSize(0)
		.tickFormat("");

    var yAxis2 = d3.svg.axis()
		.scale(y)
		.orient("left")
		.ticks(5)
		.innerTickSize(6)
		.tickPadding(-20)
		.outerTickSize(0)
		.tickFormat("");

    var xGrid = d3.svg.axis()
		.scale(x)
		.orient("bottom")
		.ticks(5)
		.tickSize(-height, -height, 0)
		.tickFormat("");

    var yGrid = d3.svg.axis()
		.scale(y)
		.orient("left")
		.ticks(5)
		.tickSize(-width, -width, 0)
		.tickFormat("");
	
    var line = d3.svg.line()
		.x(function(d) {
			return x(d.x);
		})
		.y(function(d) {
			return y(d.y);
		})
		.interpolate("linear");
	  
    var zoom = d3.behavior.zoom()
		.x(x)
		.y(y)
		.scaleExtent([1, 1])
		.on("zoom",redraw);
      
    var plotMagnitude = d3.select("#plotmagnitude").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);
	  
    // Add x grid
    plotMagnitude.append("g")
		.attr("class","x grid")
		.attr("transform","translate(0," + height + ")")
		.call(xGrid);
		

    // Add y grid
    plotMagnitude.append("g")
		.attr("class","y grid")
		.call(yGrid);

    plotMagnitude.append("g")
		.attr("class","x1 axis")
		.attr("transform","translate(0," + height + ")")
		.call(xAxis1);

    plotMagnitude.append("g")
		.attr("class","y1 axis")
		.call(yAxis1);

    /* append additional X axis */
    plotMagnitude.append("g")
		.attr("class","x2 axis")
		.attr("transform","translate(" + [0, 0] + ")")
		.call(xAxis2);

    /* append additional y axis */
    plotMagnitude.append("g")
		.attr("class","y2 axis")
		.attr("transform","translate(" + [width, 0] + ")")
		.call(yAxis2);

    // Add x axis label  
    plotMagnitude.append("text")
        .attr("transform","translate(" + (width / 2) + "," + (height + margin.bottom) + ")")
        .style("font-size","15")
        .style("text-anchor","middle")
        .text("x axis");

    // Add y axis label
    plotMagnitude.append("text")
        .attr("transform", "rotate(-90)")
        .attr("y",0 - margin.left)
        .attr("x",0 - (height / 2))
        .attr("dy", "1em")
        .style("font-size","15")
        .style("text-anchor", "middle")
        .text("y axis");

    plotMagnitude.append("defs").append("clipPath")
		.attr("id", "clip")
		.append("rect")
		.attr("width", width)
		.attr("height", height);
      
    plotMagnitude.append("rect")
		.attr("width", width)
		.attr("height", height);

    function redraw() {
		plotMagnitude.select(".x1.axis").call(xAxis1);
		plotMagnitude.select(".y1.axis").call(yAxis1);
		plotMagnitude.select(".x2.axis").call(xAxis2);
		plotMagnitude.select(".y2.axis").call(yAxis2);
		plotMagnitude.select(".x.grid").call(xGrid);
		plotMagnitude.select(".y.grid").call(yGrid);
		
		var series = [];
		var data1 = [];
		var data2 = [];
		var data3 = [];

		for (var i = 0; i < range.length; i++) {
			data1.push({
				x: range[i],
				y: leadlag(range[i])
			});

			data2.push({
				x: range[i],
				y: mag2db(math.abs(leadlag(range[i])))
			});
			
			data3.push({
				x: range[i],
				y: rad2deg(angle(leadlag(range[i])))
			});
		}

		series.push({data: data2, width: 1, color: 'blue', stroke: "0,0", legend: "MAG"  });

		var series = plotMagnitude.selectAll(".line").data(series);

		series.enter().append('path');

		series.attr("class","line")
			.attr("d",function(d) { return line(d.data); })
			.attr("stroke-width", function(d) { return d.width; })
			.style("stroke", function(d) { return d.color; })
			.style("stroke-dasharray", function(d) { return d.stroke; });
	}

	$(function() {
		redraw();
	});
</script>
</body>
</html>

【问题讨论】:

    标签: javascript d3.js plot charts


    【解决方案1】:

    您的“主要”刻度似乎被定义为带有标签的刻度,因此困难的部分变成找到它们,然后找到相应的网格线。

    怎么样:

    d3.selectAll('.x1.axis>.tick')  // find all the x axis ticks and loop
      .each(function(d,i){
        if (d3.select(this).select('text').text() === ""){ // if they have no label
          d3.select('.x.grid>.tick:nth-child(' + (i + 1) + ')') // get the corresponding grid line
            .style("stroke-dasharray", "3,3"); // and make it dashed
          }
      });
    

    完整代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <title>LOG</title>
    <meta charset="utf-8">
    <script src="//d3js.org/d3.v3.min.js"></script>
    <script src="//code.jquery.com/jquery-1.10.2.js"></script>
    <script src="//mathjs.org/js/lib/math.js"></script>
    <style type="text/css">
    svg {
        font: 10px sans-serif;
        shape-rendering: crispEdges;
    }
      
    rect {
        fill: transparent;
    }
      
    .axis path,
    .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
    }
      
    .line {
        fill: none;
        stroke: steelblue;
        stroke-width: 1.5px;
        clip-path: url(#clip);
    }
      
    .grid .tick {
        stroke: lightgrey;
        opacity: 0.7;
    }
      
    .grid path {
        stroke-width: 0;
    }
    </style>
    <script type="text/javascript">
    	function linspace(a,b,n) {
    		var every = (b-a)/(n-1),
    			range = [];
    		for (i = a; i < b; i += every)
    			range.push(i);
    		return range.length == n ? range : range.concat(b);
    	}
    	
    	function logspace(a,b,n) {
    		return linspace(a,b,n).map(function(x) { return Math.pow(10,x); });
    	}
    	
    	function isInteger(value) {
    		return typeof value === "number" && 
    			   isFinite(value) && 
    			   Math.floor(value) === value;
    	};
    	
    	function leadlag(f) {
    		w = 2*math.pi*f;
    		s = math.complex(0,w);
    
    		K  = 1;
    		fz = 20;
    		fp = 40;
    
    		wz = 2*math.pi*fz;
    		wp = 2*math.pi*fp;
    
    		return math.multiply(K,math.multiply(math.divide(wp,wz),math.divide(math.add(s,wz),(math.add(s,wp)))));
    	};
    	
    	function angle(f) {
    		return math.atan2(f.im,f.re);
    	};
    	
    	function deg2rad(deg) {
      		return deg * math.pi / 180;
    	};
     
    	function rad2deg(rad) {
      		return rad * 180 / math.pi;
    	};
    	
    	function mag2db(mag) {
    		return 20 * Math.log10(mag);
    	}
    	
    	function db2mag(db) {
    		return math.pow(10,db / 20);
    	}
    </script>
    </head>
    <body>
    <div id="plotmagnitude"></div>
    <div id="plotphase"></div>
    <script type="text/javascript">
    	var margin = {
            top: 20,
            right: 20,
            bottom: 35,
            left: 50
        };
    	
    	var width  = 450 - margin.left - margin.right;
    	var height = 250 - margin.top - margin.bottom;
    	
    	var range = logspace(0,3,1000);
    
        var x = d3.scale.log()
    		.domain([1, range[range.length-1].toFixed()])
    		.range([0, width]);
    
        var y = d3.scale.linear()
    		.domain([-10, 2])
    		.range([height, 0]);
    
        var xAxis1 = d3.svg.axis()
    		.scale(x)
    		.orient("bottom")
    		.ticks(1,"0.1s")
    		.innerTickSize(-6)
    		.outerTickSize(0)
    		.tickPadding(7);
    
        var yAxis1 = d3.svg.axis()
    		.scale(y)
    		.orient("left")
    		.ticks(5)
    		.innerTickSize(-6)
    		.outerTickSize(0)
    		.tickPadding(7);
    
        var xAxis2 = d3.svg.axis()
    		.scale(x)
    		.orient("top")
    		.ticks(5)
    		.innerTickSize(-6)
    		.tickPadding(-20)
    		.outerTickSize(0)
    		.tickFormat("");
    
        var yAxis2 = d3.svg.axis()
    		.scale(y)
    		.orient("left")
    		.ticks(5)
    		.innerTickSize(6)
    		.tickPadding(-20)
    		.outerTickSize(0)
    		.tickFormat("");
    
        var xGrid = d3.svg.axis()
    		.scale(x)
    		.orient("bottom")
    		.ticks(5)
    		.tickSize(-height, -height, 0)
    		.tickFormat("");
    
        var yGrid = d3.svg.axis()
    		.scale(y)
    		.orient("left")
    		.ticks(5)
    		.tickSize(-width, -width, 0)
    		.tickFormat("");
    	
        var line = d3.svg.line()
    		.x(function(d) {
    			return x(d.x);
    		})
    		.y(function(d) {
    			return y(d.y);
    		})
    		.interpolate("linear");
    	  
        var zoom = d3.behavior.zoom()
    		.x(x)
    		.y(y)
    		.scaleExtent([1, 1])
    		.on("zoom",redraw);
          
        var plotMagnitude = d3.select("#plotmagnitude").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);
    	  
        // Add x grid
        plotMagnitude.append("g")
    		.attr("class","x grid")
    		.attr("transform","translate(0," + height + ")")
    		.call(xGrid);
    
        // Add y grid
        plotMagnitude.append("g")
    		.attr("class","y grid")
    		.call(yGrid);
    
        plotMagnitude.append("g")
    		.attr("class","x1 axis")
    		.attr("transform","translate(0," + height + ")")
    		.call(xAxis1);
    
        plotMagnitude.append("g")
    		.attr("class","y1 axis")
    		.call(yAxis1);
    
        /* append additional X axis */
        plotMagnitude.append("g")
    		.attr("class","x2 axis")
    		.attr("transform","translate(" + [0, 0] + ")")
    		.call(xAxis2);
    
        /* append additional y axis */
        plotMagnitude.append("g")
    		.attr("class","y2 axis")
    		.attr("transform","translate(" + [width, 0] + ")")
    		.call(yAxis2);
    
        // Add x axis label  
        plotMagnitude.append("text")
            .attr("transform","translate(" + (width / 2) + "," + (height + margin.bottom) + ")")
            .style("font-size","15")
            .style("text-anchor","middle")
            .text("x axis");
    
        // Add y axis label
        plotMagnitude.append("text")
            .attr("transform", "rotate(-90)")
            .attr("y",0 - margin.left)
            .attr("x",0 - (height / 2))
            .attr("dy", "1em")
            .style("font-size","15")
            .style("text-anchor", "middle")
            .text("y axis");
    
        plotMagnitude.append("defs").append("clipPath")
    		.attr("id", "clip")
    		.append("rect")
    		.attr("width", width)
    		.attr("height", height);
          
        plotMagnitude.append("rect")
    		.attr("width", width)
    		.attr("height", height);
    
        function redraw() {
    		plotMagnitude.select(".x1.axis").call(xAxis1);
    		plotMagnitude.select(".y1.axis").call(yAxis1);
    		plotMagnitude.select(".x2.axis").call(xAxis2);
    		plotMagnitude.select(".y2.axis").call(yAxis2);
    		plotMagnitude.select(".x.grid").call(xGrid);
    		plotMagnitude.select(".y.grid").call(yGrid);
    		
    		var series = [];
    		var data1 = [];
    		var data2 = [];
    		var data3 = [];
    
    		for (var i = 0; i < range.length; i++) {
    			data1.push({
    				x: range[i],
    				y: leadlag(range[i])
    			});
    
    			data2.push({
    				x: range[i],
    				y: mag2db(math.abs(leadlag(range[i])))
    			});
    			
    			data3.push({
    				x: range[i],
    				y: rad2deg(angle(leadlag(range[i])))
    			});
    		}
    
    		series.push({data: data2, width: 1, color: 'blue', stroke: "0,0", legend: "MAG"  });
    
    		var series = plotMagnitude.selectAll(".line").data(series);
    
    		series.enter().append('path');
    
    		series.attr("class","line")
    			.attr("d",function(d) { return line(d.data); })
    			.attr("stroke-width", function(d) { return d.width; })
    			.style("stroke", function(d) { return d.color; })
    			.style("stroke-dasharray", function(d) { return d.stroke; });
    			
    		d3.selectAll('.x1.axis>.tick')
    		  .each(function(d,i){
      		  if (d3.select(this).select('text').text() === ""){
      		    d3.select('.x.grid>.tick:nth-child(' + (i + 1) + ')')
      		      .style("stroke-dasharray", "3,3");
      		  }
    		  });
    		
    	}
    
    	$(function() {
    		redraw();
    	});
    </script>
    </body>
    </html>

    【讨论】:

    • 感谢您的回复!一开始我对这个答案很满意。但是,我正在创建一个波特图。这包括两个位于彼此下方的地块。幅度图(上面的图)通常没有 x 轴标签以节省空间,并且因为它与下面的图重复,即相位图。在这个解决方案之后我要做的下一步是创建两个图,一个没有 x 标签,一个下面有 x 标签。我也愿意接受改进代码的建议,因为我觉得它有点臃肿?
    • @WG-,如果两个图上的 x 轴匹配,则此解决方案仍然有效。您使用标签循环轴并从 both 图中选择相应的网格线。
    • 我刚刚尝试了你的解决方案,当我有两个情节都可以工作时; plnkr.co/edit/HE2f7JLZOjXV0JLf7iGY?p=preview,但它似乎不起作用?它不选择其他 x 轴刻度?
    • 使用d3.selectAll('.x.grid&gt;.tick:nth-child(' + (i + 1) + ')').style("stroke-dasharray", "3,3");修复它
    猜你喜欢
    • 2017-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-03
    • 2020-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多