2014-12-18 94 views
2

我的SVG地图有问题。 我使用jVectorMap来创建一个自定义地图,我需要在字段的中心写每个字段的名称。 的例子是:JSFiddle Example(在右侧变焦看文字)SVG发现路径的旋转角度

我能找到的每一个领域具有这种功能中心:

jvm.Map.prototype.getRegionCentroid = function(region){ 

    if(typeof region == "string") 
     region = this.regions[region.toUpperCase()]; 

    var bbox = region.element.shape.getBBox(), 
    xcoord = (bbox.x + bbox.width/2), 
    ycoord = (bbox.y + bbox.height/2); 

    return [xcoord, ycoord]; 
}; 

但我的问题是,我想旋转用于将其与相关字段的顶行对齐的文本。 我试过用getCTM()函数,但它给了我每个字段总是相同的值。

我怎样才能找到每个领域的正确的旋转角度?

谢谢大家!

回答

2

貌似squeamish ossifrage击败了我这一个,和他们说过会正是我的做法太...

解决方案

本质上找到EAC最长的线段h区域的路径,然后定位您的文本以与该线段对齐,同时尝试确保文本不会颠倒(!)

enter image description here

这里有一个sample jsfiddle

在我添加标签,所有区域的小提琴的$(document).ready()功能,但你会注意到,一些地区的有不在该区域内的质心或导致问题的非直线边缘 - 稍微修改地图可能是最简单的修复方法。

说明

这里有3个功能,我写证明的原则:

  • addOrientatedLabel(regionName) - 增加了一个标签,地图的指定区域。
  • getAngleInDegreesFromRegion(regionName) - 获取区域
  • getLengthSquared(startPt,endPt)的最长边缘的角度 - 得到长度的平方线SEG(比让长度更有效)的。

addOrientatedLabel()地方使用平移变换和旋转的文本以相同的角度在该区域的最长线段在质心的标签。在SVG变换得到解决从右到左使:

transform="translate(x,y) rotate(45)" 

被解释为旋转第一,然后翻译。这个顺序很重要!

它也使用text-anchor="middle"dominant-baseline="middle",如squeamish ossifrage所解释的。未能这样做会导致文本在其区域内错位。

getAngleInDegreesFromRegion()是所有工作都完成的地方。它通过选择器获取区域的SVG路径,然后遍历路径中的每个点。只要发现某个点是线段的一部分(而不是移动到或其他指令),它就会计算线段的平方长度。如果线段的平方长度是迄今为止最长的,则存储其细节。我使用平方长度,因为这节省了执行平方根操作(它仅用于比较目的,所以平方长度很好)。

请注意,我将longestLine数据初始化为水平数据,因此如果该区域根本没有线段,至少会获得水平文本。

一旦我们有最长的线,我计算它的相对于x轴的角度与Math.atan2,并将其从弧度转换为度为SVG与(angle/Math.PI) * 180。最后的技巧是确定角度是否将文本颠倒,如果是,则旋转另一个180度。

注意

我没有以前那么我的SVG的代码可能不是最佳使用SVG,但它的测试,它的工作原理上是由主要的直线段所有地区 - 你需要添加错误检查课程的生产应用!

代码

function addOrientatedLabel(regionName) { 

    var angleInDegrees = getAngleInDegreesFromRegion(regionName); 

    var map = $('#world-map').vectorMap('get', 'mapObject'); 
    var coords = map.getRegionCentroid(regionName); 

    var svg = document.getElementsByTagName('g')[0]; //Get svg element 
    var newText = document.createElementNS("http://www.w3.org/2000/svg","text"); 
    newText.setAttribute("font-size","4"); 
    newText.setAttribute("text-anchor","middle"); 
    newText.setAttribute("dominant-baseline","middle"); 
    newText.setAttribute('font-family', 'MyriadPro-It'); 
    newText.setAttribute('transform', 'translate(' + coords[0] + ',' + coords[1] + ') rotate(' + angleInDegrees + ')'); 
    var textNode = document.createTextNode(regionName); 
    newText.appendChild(textNode); 

    svg.appendChild(newText);  
} 

这里是我的方法来找到一个给定的地图区域路径最长的线段:

function getAngleInDegreesFromRegion(regionName) { 

    var svgPath = document.getElementById(regionName); 

    /* longest edge will default to a horizontal line */ 
    /* (in case the shape is degenerate): */ 
    var longestLine = { startPt: {x:0, y:0}, endPt: {x:100,y:0}, lengthSquared : 0 }; 

    /* loop through all the points looking for the longest line segment: */ 
    for (var i = 0 ; i < svgPath.pathSegList.numberOfItems-1; i++) { 
     var pt0 = svgPath.pathSegList.getItem(i); 
     var pt1 = svgPath.pathSegList.getItem(i+1); 
     if (pt1.pathSegType == SVGPathSeg.PATHSEG_LINETO_ABS) { 
      var lengthSquared = getLengthSquared(pt0, pt1); 
      if(lengthSquared > longestLine.lengthSquared) { 
       longestLine = { startPt:pt0, endPt:pt1, lengthSquared:lengthSquared};     
      }    
     }/* end if dealing with line segment */     
    }/* end loop through all pts in svg path */  

    /* determine angle of longest line segement relative to x axis */ 
    var dY = longestLine.startPt.y - longestLine.endPt.y; 
    var dX = longestLine.startPt.x - longestLine.endPt.x; 
    var angleInDegrees = (Math.atan2(dY,dX)/Math.PI * 180.0); 

    /* if text would be upside down, rotate through 180 degrees: */ 
    if((angleInDegrees > 90 && angleInDegrees < 270) || (angleInDegrees < -90 && angleInDegrees > -270)) { 
     angleInDegrees += 180; 
     angleInDegrees %= 360; 
    }  

    return angleInDegrees; 
} 

注意,我getAngleInDegreesFromRegion()方法考虑最长直如果它是使用PATHSEG_LINETO_ABS SVG命令创建的,则在路径中行...您需要更多的功能来处理不包含直的区域线。您可以通过处理与曲线为直线近似:

if (pt1.pathSegType != SVGPathSeg.PATHSEG_MOVETO_ABS) 

但会有一些极端情况,所以修改你的地图数据可能是最简单的方法。

最后,以下是完整强制性平方距离方法:

function getLengthSquared(startPt, endPt) { 
    return ((startPt.x - endPt.x) * (startPt.x - endPt.x)) + ((startPt.y - endPt.y) * (startPt.y - endPt.y)); 
} 

希望这是很清楚,以帮助您开始。

+0

解决质心位于区域中心之外的区域可能是最困难的问题。如果您将所有曲线视为直线曲线,则即使在存在曲线边缘的区域,它也会排列文本以“确定”工作。如上所述,对地图数据进行一些修改(如果可能的话)可能是对那些效果不佳的地区最简单的修复方法。 – 2014-12-20 21:19:30

+0

我有正确的功能来解决某些SVG路径上的问题。 问题是某些路径是用相对值表示的,所以我在StackOverflow中添加了这个答案中的函数: [convert-svg-path-to-absolute-commands](http://stackoverflow.com/questions/ (svgPath.getAttribute(“d”)9677885/convert-svg-path-to-absolute-commands) 然后我添加了行来检查路径是否相对并最终调用convertToAbsolute()函数: if(svgPath.getAttribute .search(“l”)> -1){convertToAbsolute(svgPath);} } 请为未来添加此修改。 非常感谢你! – 2014-12-20 21:24:46

+0

这是JSFiddle上的正确示例 http://jsfiddle.net/Lee53kwr/27/ – 2014-12-20 21:27:26

2

查询getCTM()不会帮助。所有给你的是形状坐标系统的变换矩阵(如你所发现的那样,每个形状都是相同的)。要获得形状的顶点坐标,您必须检查region.element.shape.pathSegList的内容。

这会变得凌乱。虽然很多形状是使用简单的“移至”和“换行”命令绘制的,但有些使用相对坐标和其他类型的线。我注意到至少有一个三次曲线。可能值得寻找一个SVG顶点操作库来让生活更轻松。

但是一般而言,您需要做的是获取每个形状的坐标列表(在必要时将相对坐标转换为绝对坐标),并找到最长长度的线段。请注意,这可能是该线的两个端点之间的部分。您可以从Math.atan2(y_end-y_start,x_end-x_start)轻松找到该细分市场的方向。

在旋转文本时,通过使用<g>元素和transform=translate()属性将坐标原点移动到文本所需的位置,使生活变得轻松自如。然后,当您为其添加transform=rotate()属性时,文本将不会触及距离。此外,请使用text-anchor="middle"dominant-baseline="middle"将文本居中放置在所需的位置。

你的代码应该结束了看起来像这样:

var svg = document.getElementsByTagName('g')[0]; //Get svg element 
var shape_angle = get_orientation_of_longest_segment(svg.pathSegList); //Write this function 
var newGroup = document.createElementNS("http://www.w3.org/2000/svg","g"); 
var newText = document.createElementNS("http://www.w3.org/2000/svg","text"); 
newGroup.setAttribute("transform", "translate("+coords[0]+","+coords[1]+")"); 
newText.setAttribute("font-size","4"); 
newText.setAttribute("text-anchor","middle"); 
newText.setAttribute("dominant-baseline","middle"); 
newText.setAttribute("transform","rotate("+shape_angle+")"); 
newText.setAttribute('font-family', 'MyriadPro-It'); 
var textNode = document.createTextNode("C1902"); 
newText.appendChild(textNode); 
newGroup.appendChild(newText); 
svg.appendChild(newGroup);