d3.js画拓扑图

<%#
LuCI - Lua Configuration Interface
Copyright 2012 Jo-Philipp Wich <xm@subsignal.org>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

$Id$

-%>

<% luci.http.prepare_content("text/html") %>

<%+header%>
<style>
.topoWarp{ position:relative; height:600px; width:800px;margin:20px auto 0;}
.topoWarp .tab{ position:absolute; top:20px; right:20px; z-index:200}
.topoWarp .tab li{ float:left; list-style:none; cursor:pointer; margin-left:10px; }
.topoWarp .tab li.current,.topoWarp .tab li:hover{ color:#0099CC;}
</style>
<div id="ID_DIV_TopoWarp" class="topoWarp">
     <ul id="ID_UL_RateSignal" style="display:none" class="tab">
        <li class="current">速率</li>
        <li>信号</li>
    </ul>
    <div id="ID_DIV_Unlicensed" style="display:none" class="alert-message warning"> 
        <h3>设备未激活</h3>
    </div>
</div>
<script type="text/javascript" src="<%=resource%>/jquery-1.9.1.js?v=git-19.044.11045-4a8bfd0"></script>
<script type="text/javascript" src="<%=resource%>/d3.min.js"></script>



<!-- 全局变量定义 -->
<script type="text/javascript">
    var g_svgHeight = $(".topoWarp").height(); //整个svg的高度
    var g_svgWidth = $(".topoWarp").width();//整个svg的宽度
    var g_interval;
    var g_rectWidth = 100;
    var g_rectHeight = 40;
</script>



<script type="text/javascript">//<![CDATA[
    /***********************************************************
    * 描述: 根据拓扑信息,生成坐标信息
    * 入参: 无
    * 出参: 无
    * 返回: 无
    ***********************************************************/
    function GeneratePosition(topoArray)
    {
        var r = 260;
        var offset = g_svgHeight/2; //因为svg的原点在左上角,所以计算出来的坐标值要加上偏移
        var x, y;

        if(topoArray.length == 1)
        {
            topoArray[0].x = r;
            topoArray[0].y = r;
            topoArray[0].angle = 0;
        }
        else
        {
            var radians = (Math.PI / 180) * Math.round(360 / topoArray.length); //弧度

            for(var i = 0; i < topoArray.length; i++)
            {
                x = offset + r * Math.cos(radians * i);
                y = offset + r * Math.sin(radians * i);
                topoArray[i].x = x;
                topoArray[i].y = y;
                topoArray[i].angle = i*Math.round(360 / topoArray.length);//角度
            }
        }
    }


    /***********************************************************
    * 描述: 画节点
    * 入参: svg - 用于画节点的画布
    * 出参: 无
    * 返回: 无
    ***********************************************************/
    function DrawNodes(topoArray, svg)
    {
        var color = "SteelBlue";

        for(i=0; i<topoArray.length; i++)
        {
            /* 画带弧度的矩形 */
            svg.append("rect")
                .attr("x", topoArray[i].x) //x坐标
                .attr("y", topoArray[i].y) //y坐标
                .attr("width", g_rectWidth) //宽
                .attr("height", g_rectHeight) //宽
                .attr("stroke", "black")
                .attr("rx",10)
                .attr("fill", color);

            /* 在矩形中文字的坐标 */
            var textX = topoArray[i].x + g_rectWidth/2; //文字的x坐标
            var textY = topoArray[i].y;

            /* 在矩形中显示ip和底噪 */
            var text = svg.append("text")
                .attr("text-anchor", "middle" )
                .attr("x", textX)
                .attr("y", textY)
                .attr("font-size", "14");
            text.append("tspan")
                .attr("x", textX)
                .attr("dy", "1.2em")//使用1.2,上面留一点
                .text(topoArray[i].ip);
            text.append("tspan")
                .attr("x", textX)
                .attr("dy", "1.2em") //使用1.2,上面留一点
                .text("(" + topoArray[i].noise + "dBm)");
         }
    }

    function AdjustCoordinate(topoArray, myTopoIndex, neighTopoIndex, x, y)
    {
        var topoObject = topoArray[myTopoIndex];
        var retObject = new Object();
        var adjust;
        var neighCount = topoObject.neigh.length;


        /* 将节点分成4个区域,315度-45度为第一个区域,以此类推 */
        if((topoObject.angle >= 0 && topoObject.angle<45) || (topoObject.angle>=315 && topoObject.angle<360)) 
        {
            if(neighCount == 1) //如果只有1个邻居,则选矩形宽的中点
            {
                retObject.x = x;
                retObject.y = y + g_rectHeight/2;
            }
            else
            {
                adjust = g_rectHeight/(neighCount-1);
                retObject.x = x;
                if(neighTopoIndex<myTopoIndex)
                    retObject.y = y + adjust*(neighCount - neighTopoIndex-1);
                else
                    retObject.y = y + adjust*(neighCount - neighTopoIndex);
            }
        }
        else if(topoObject.angle>=45 && topoObject.angle<125)
        {
            adjust = g_rectWidth/(neighCount-1);
            if(neighTopoIndex<myTopoIndex)
                retObject.x = x + adjust*(neighCount - neighTopoIndex - 1);
            else
                retObject.x = x + adjust*(neighTopoIndex-myTopoIndex-1);
            retObject.y = y;
        }
        else if(topoObject.angle>=125 && topoObject.angle<215)
        {
            if(neighCount == 1) //如果只有1个邻居,则选矩形宽的中点
            {
                retObject.x = x + g_rectWidth;
                retObject.y = y + g_rectHeight/2;
            }
            else
            {
                adjust = g_rectHeight/(neighCount-1);
                retObject.x = x + g_rectWidth;
                if(neighTopoIndex<myTopoIndex)
                    retObject.y = y + adjust*(neighTopoIndex+1);
                else
                    retObject.y = y + adjust*(neighCount - neighTopoIndex);
            }
        }
        else
        {
            adjust = g_rectWidth/(neighCount-1);
            if(neighTopoIndex<myTopoIndex)
                retObject.x = x + adjust*(myTopoIndex - neighTopoIndex-1);
            else
                retObject.x = x + adjust*(neighCount - neighTopoIndex);
            retObject.y = y + g_rectHeight;
        }

        return retObject;
    }

    function DrawConnector(topoArray, svg, type)
    {
        var path;
        var ipArray = new Array();
        var id;
        var textPathId;
        var textOffset;
        var pathColor = "SteelBlue";

        path = "M2,2 L10,6 L2,10 L6,6 L2,2";
        svg.append("marker")
            .attr("id", "arrow")
            .attr("markerUnits", "strokeWidth")
            .attr("markerWidth", "12")
            .attr("markerHeight", "12")
            .attr("viewBox", "0 0 12 12")
            .attr("refX", "6")
            .attr("refY", "6")
            .attr("orient", "auto")
            .append("path")
            .attr("d", path)
            .attr("fill", pathColor);

        for(var i=0; i<topoArray.length; i++)
        {
            ipArray.push(topoArray[i].ip);
        }

        for(var i=0; i<topoArray.length; i++)
        {
            for(var j=0; j<topoArray[i].neigh.length; j++)
            {
                var fromIndex = ipArray.indexOf(topoArray[i].neigh[j].ip);
                var adjustObject;
                var fromX, fromY, toX, toY;
                var adjustX, adjustY;

                fromX = topoArray[fromIndex].x;
                fromY = topoArray[fromIndex].y;
                toX = topoArray[i].x;
                toY = topoArray[i].y;

                adjustObject = AdjustCoordinate(topoArray, fromIndex, i, fromX, fromY);
                fromX = Math.round(adjustObject.x);//取整,避免小数不同
                fromY = Math.round(adjustObject.y);//取整,避免小数不同

                adjustObject = AdjustCoordinate(topoArray, i, fromIndex, toX, toY);
                toX = Math.round(adjustObject.x);//取整,避免小数不同
                toY = Math.round(adjustObject.y);//取整,避免小数不同

                if(fromX == toX)
                {
                    if(fromY > toY)
                    {
                        adjustX = 40;
                        adjustY = 0;
                        fromX += 10;
                        toX += 10;
                    }
                    else
                    {
                        adjustX = -40;
                        adjustY = 0;
                    }
                }
                else if(fromX < toX)
                {
                    adjustX = 0;
                    adjustY = 40;
                    if(fromY == toY)
                    {
                        fromY += 5;
                        toY += 5;
                    }
                    else if(fromY > toY)
                    {
                        toX += 10;
                    }
                    else
                    {
                        fromX -= 10;
                    }
                }
                else
                {
                    adjustX = 0;
                    adjustY = -40;
                    if(fromY == toY)
                    {
                        //什么都不做
                    }
                    else if(fromY < toY)
                    {
                        toX -= 10;
                    }
                    else
                    {
                        fromX += 10
                    }
                }

                /* 画箭头 */
                path = "M" + fromX + ',' + fromY + " S" + ((fromX + toX)/2 + adjustX) + ',' + ((fromY + toY) / 2 + adjustY) + "," + toX + ',' + toY;
                id = "ID_PATH_" + fromIndex + '_'  + i;
                svg.append("path")
                    .attr("id", id)
                    .attr("d", path)
                    .attr("fill", "none") //只画线,不填充
                    .attr("stroke", pathColor)
                    .attr("stroke-width", 2)
                    .attr("marker-end", "url(#arrow)");

                if(fromX > toX)
                {
                    textPathId = "ID_PATH_Text" + i + "_" + fromIndex;
                    path = "M" + toX + ',' + toY + " S" + ((fromX + toX)/2 + adjustX) + ',' + ((fromY + toY) / 2 + adjustY) + "," + fromX + ',' + fromY;
                    svg.append("defs")
                    .append("path")
                    .attr("id", textPathId)
                    .attr("d", path);
                    textOffset = "30%";
                }
                else
                {
                    textPathId = id;
                    textOffset = "80%";
                }

                svg.append("text")
                    .append("textPath")
                    // 给textPath设置path的引用
                    .attr("xlink:href", d => {
                        return "#"+textPathId;
                    })
                    .style("text-anchor", "end")
                    .attr("startOffset", textOffset)
                    .style("fill", pathColor)
                    .style("font-size", "16px")
                    .text(text => {
                        if(type == "rate")
                            return topoArray[i].neigh[j].rate + "Mbps";
                        else
                            return topoArray[i].neigh[j].signal + "dBm";
                    });
            }
        }
    }

    /***********************************************************
    * 描述: 根据节点的坐标和拓扑信息画图
    * 入参: 无
    * 出参: 无
    * 返回: 无
    ***********************************************************/
    function DrawAll(topoArray, type)
    {
        if ($("#ID_SVG_Topo").length)
            $("#ID_SVG_Topo").remove();

        var svg = d3.select('#ID_DIV_TopoWarp')
            .append("svg")
            .attr("id", "ID_SVG_Topo")
            .attr("width", g_svgWidth) //设定宽度
            .attr("height", g_svgHeight);	//设定高度;

        DrawNodes(topoArray, svg);
        DrawConnector(topoArray, svg, type);
        return;
    }


    /***********************************************************
    * 描述: 定时器处理函数,定时获取拓扑数据并刷新拓扑图
    * 入参: type - 当前显示速率还是信号强度
    * 出参: 无
    * 返回: 无
    ***********************************************************/
    function IntervalHandle(type)
    {
        XHR.get('<%=luci.dispatcher.build_url("gettopology/topo/json")%>', null,
            function(x, data)
            {
                var topoArray = data.results.topology;
                if(data.status == "success" || data.status == "working")
                {
                    $("#ID_DIV_Unlicensed").hide();
                    $("#ID_UL_RateSignal").show();

                    GeneratePosition(topoArray);
                    //quneeJson(topoArray, type);
                    DrawAll(topoArray, type);
                }
                else if(data.status == "unlicensed")
                {
                    $("#ID_DIV_Unlicensed").show();
                    $("#ID_UL_RateSignal").hide();
                }
            }
        );
    }


    /***********************************************************
    * 描述: 速率和信号强度点击响应函数
    * 入参: 无
    * 出参: 无
    * 返回: 无
    ***********************************************************/
    $(".topoWarp .tab li").click(function()
    {
        var refreshTime = 3000; //每3秒刷新一次

        $(".topoWarp .tab li").removeClass("current");
        $(this).addClass("current");

        clearInterval(g_interval);

        if($(this).index() == 0)
        {
            IntervalHandle("rate");	
            g_interval = setInterval(function(){ IntervalHandle("rate")}, refreshTime);
            
        }
        else
        {
            IntervalHandle("signal");	
            g_interval = setInterval(function(){ IntervalHandle("signal") }, refreshTime);
        }
    })


    /* 第一次进入页面模拟点击 */
    $(".topoWarp .tab li:first").click();
//]]>
</script>


<%+footer%>

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值