2014-12-18 171 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);