2 ECMAScript helper functions
3 Copyright (C) <2004> <Andreas Neumann>
4 Version 1.1, 2004-11-18
5 neumann@karto.baug.ethz.ch
7 http://www.carto.net/neumann/
9 Credits: numerous people on svgdevelopers@yahoogroups.com
11 This ECMA script library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 2.1 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public
22 License along with this library (http://www.carto.net/papers/svg/resources/lesser_gpl.txt); if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 original document site: http://www.carto.net/papers/svg/resources/helper_functions.js
28 Please contact the author in case you want to use code or ideas commercially.
29 If you use this code, please include this copyright header, the included full
30 LGPL 2.1 text and read the terms provided in the LGPL 2.1 license
31 (http://www.gnu.org/copyleft/lesser.txt)
33 -------------------------------
35 Please report bugs and send improvements to neumann@karto.baug.ethz.ch
36 If you use these scripts, please link to the original (http://www.carto.net/papers/svg/navigationTools/)
37 somewhere in the source-code-comment or the "about" of your project and give credits, thanks!
41 //global variables necessary to create elements in these namespaces, do not delete them!!!!
42 var svgNS
= "http://www.w3.org/2000/svg";
43 var xlinkNS
= "http://www.w3.org/1999/xlink";
44 var cartoNS
= "http://www.carto.net/attrib";
45 var attribNS
= "http://www.carto.net/attrib";
46 var batikNS
= "http://xml.apache.org/batik/ext";
48 /* ----------------------- helper functions to calculate stuff ---------------- */
49 /* ---------------------------------------------------------------------------- */
50 function toPolarDir(xdiff
,ydiff
) { // Subroutine for calculating polar Coordinates
51 direction
= (Math
.atan2(ydiff
,xdiff
));
52 //result is angle in radian
56 function toPolarDist(xdiff
,ydiff
) { // Subroutine for calculating polar Coordinates
57 distance
= Math
.sqrt(xdiff
* xdiff
+ ydiff
* ydiff
);
61 function toRectX(direction
,distance
) { // Subroutine for calculating cartesic coordinates
62 x
= distance
* Math
.cos(direction
);
63 y
= distance
* Math
.sin(direction
);
67 function toRectY(direction
,distance
) { // Subroutine for calculating cartesic coordinates
68 x
= distance
* Math
.cos(direction
);
69 y
= distance
* Math
.sin(direction
);
73 //Converts degrees to radians.
74 function DegToRad(deg
) {
75 return (deg
/ 180.0 * Math
.PI
);
78 //Converts radians to degrees.
79 function RadToDeg(rad
) {
80 return (rad
/ Math
.PI
* 180.0);
83 //converts decimal degrees to degree/minutes/seconds
85 var minutes
= (Math
.abs(dd
) - Math
.floor(Math
.abs(dd
))) * 60;
86 var seconds
= (minutes
- Math
.floor(minutes
)) * 60;
87 var minutes
= Math
.floor(minutes
);
88 var degrees
= Math
.floor(dd
);
89 return {deg
:degrees
,min
:minutes
,sec
:seconds
};
92 //converts degree/minutes/seconds to decimal degrees
93 function dms2dd(deg
,min
,sec
) {
94 return deg
+ (min
/ 60) + (sec
/ 3600);
97 //log functions that do not exist in Math object
100 return Math
.log(x
)/Math
.log(b
);
103 //gets 4 z-values (4 corners), a position, delta x and delty and a cellsize as input and returns interpolated z-value
104 function intBilinear(za
,zb
,zc
,zd
,xpos
,ypos
,ax
,ay
,cellsize
) { //bilinear interpolation function
105 e
= (xpos
- ax
) / cellsize
;
106 f
= (ypos
- ay
) / cellsize
;
108 //calculation of weights
109 wa
= (1 - e
) * (1 - f
);
114 height_interpol
= wa
* zc
+ wb
* zd
+ wc
* za
+ wd
* zb
;
116 return (height_interpol
);
119 //test if point is left of or right of, result is 1 (leftof) or 0 (rightof)
120 function leftOfTest(pointx
,pointy
,linex1
,liney1
,linex2
,liney2
) {
121 result
= (liney1
- pointy
) * (linex2
- linex1
) - (linex1
- pointx
) * (liney2
- liney1
);
123 leftof
= 1; //case left of
126 leftof
= 0; //case left of
131 //input is point coordinate, and 2 line coordinates
132 function distFromLine(xpoint
,ypoint
,linex1
,liney1
,linex2
,liney2
) {
133 dx
= linex2
- linex1
;
134 dy
= liney2
- liney1
;
135 distance
= (dy
* (xpoint
- linex1
) - dx
* (ypoint
- liney1
)) / Math
.sqrt(Math
.pow(dx
,2) + Math
.pow(dy
,2));
139 //converts radian value to degrees
140 function radian2deg(radian
) {
141 deg
= radian
/ Math
.PI
* 180;
145 //input is two vectors (a1,a2 is vector a, b1,b2 is vector b), output is angle in radian
146 //Formula: Acos from Scalaproduct of the two vectors divided by ( norm (deutsch Betrag) vector 1 by norm vector 2
147 //see http://www.mathe-online.at/mathint/vect2/i.html#Winkel
148 function angleBetwTwoLines(a1
,a2
,b1
,b2
) {
149 angle
= Math
.acos((a1
* b1
+ a2
* b2
) / (Math
.sqrt(Math
.pow(a1
,2) + Math
.pow(a2
,2)) * Math
.sqrt(Math
.pow(b1
,2) + Math
.pow(b2
,2))));
153 //input is two vectors (a1,a2 is vector a, b1,b2 is vector b), output is new vector c2 returned as array
154 //Formula: Vektor a divided by Norm Vector a (Betrag) plus Vektor b divided by Norm Vector b (Betrag)
155 //see http://www.mathe-online.at/mathint/vect1/i.html#Winkelsymmetrale
156 function calcBisectorVector(a1
,a2
,b1
,b2
) {
157 betraga
= Math
.sqrt(Math
.pow(a1
,2) + Math
.pow(a2
,2));
158 betragb
= Math
.sqrt(Math
.pow(b1
,2) + Math
.pow(b2
,2));
160 c
[0] = a1
/ betraga
+ b1
/ betragb
;
161 c
[1] = a2
/ betraga
+ b2
/ betragb
;
165 //input is two vectors (a1,a2 is vector a, b1,b2 is vector b), output is angle in radian
166 //Formula: Vektor a divided by Norm Vector a (Betrag) plus Vektor b divided by Norm Vector b (Betrag)
167 //see http://www.mathe-online.at/mathint/vect1/i.html#Winkelsymmetrale
168 function calcBisectorAngle(a1
,a2
,b1
,b2
) {
169 betraga
= Math
.sqrt(Math
.pow(a1
,2) + Math
.pow(a2
,2));
170 betragb
= Math
.sqrt(Math
.pow(b1
,2) + Math
.pow(b2
,2));
171 c1
= a1
/ betraga
+ b1
/ betragb
;
172 c2
= a2
/ betraga
+ b2
/ betragb
;
173 angle
= toPolarDir(c1
,c2
);
177 function intersect2lines(line1x1
,line1y1
,line1x2
,line1y2
,line2x1
,line2y1
,line2x2
,line2y2
) {
178 //formula see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
179 var result
= new Array();
180 var denominator
= (line2y2
- line2y1
)*(line1x2
- line1x1
) - (line2x2
- line2x1
)*(line1y2
- line1y1
);
181 if (denominator
== 0) {
182 alert("lines are parallel");
185 ua
= ((line2x2
- line2x1
)*(line1y1
- line2y1
) - (line2y2
- line2y1
)*(line1x1
- line2x1
)) / denominator
;
186 ub
= ((line1x2
- line1x1
)*(line1y1
- line2y1
) - (line1y2
- line1y1
)*(line1x1
- line2x1
)) / denominator
;
188 result
["x"] = line1x1
+ ua
* (line1x2
- line1x1
);
189 result
["y"] = line1y1
+ ua
* (line1y2
- line1y1
);
193 /* ----------------------- helper function to sort arrays ---------------- */
194 /* ----------------------------------------------------------------------- */
195 //my own sort function, uses only first part of string (population value)
196 function mySort(a
,b
) {
197 var myResulta
= a
.split("+");
198 var myResultb
= b
.split("+");
199 if (parseFloat(myResulta
[0]) < parseFloat(myResultb
[0])) {
207 /* ----------------------- helper function format number strings -------------- */
208 /* ---------------------------------------------------------------------------- */
209 //formatting number strings
210 //this function add's "'" to a number every third digit
211 function formatNumberString(myString
) {
212 //check if of type string, if number, convert it to string
213 if (typeof(myString
) == "number") {
214 myTempString
= myString
.toString();
217 myTempString
= myString
;
220 //if it contains a comma, it will be split
221 var splitResults
= myTempString
.split(".");
222 var myCounter
= splitResults
[0].length
;
224 while(myCounter
> 0) {
226 myNewString
= "," + splitResults
[0].substr(myCounter
- 3,3) + myNewString
;
229 myNewString
= splitResults
[0].substr(0,myCounter
) + myNewString
;
235 myNewString
= splitResults
[0];
237 //concatenate if it contains a comma
238 if (splitResults
[1]) {
239 myNewString
= myNewString
+ "." + splitResults
[1];
244 //function for status Bar
245 function statusChange(statusText
) {
246 document
.getElementById("statusText").firstChild
.nodeValue
= "Statusbar: " + statusText
;
250 function scaleObject(evt
,factor
) {
251 //reference to the currently selected object
252 var element
= evt
.currentTarget
;
253 var myX
= element
.getAttributeNS(null,"x");
254 var myY
= element
.getAttributeNS(null,"y");
255 var newtransform
= "scale(" + factor
+ ") translate(" + (myX
* 1 / factor
- myX
) + " " + (myY
* 1 / factor
- myY
) +")";
256 element
.setAttributeNS(null,'transform', newtransform
);
259 //this code is copied from Kevin Lindsey
260 //http://www.kevlindev.com/tutorials/basics/transformations/toUserSpace/index.htm
261 function getTransformToRootElement(node
) {
263 //this part is for fully conformant players
264 var CTM
= node
.getTransformToElement(document
.documentElement
);
267 //this part is for ASV3 or other non-conformant players
268 // Initialize our CTM the node's Current Transformation Matrix
269 var CTM
= node
.getCTM();
270 // Work our way through the ancestor nodes stopping at the SVG Document
271 while ( ( node
= node
.parentNode
) != document
) {
272 // Multiply the new CTM to the one with what we have accumulated so far
273 CTM
= node
.getCTM().multiply(CTM
);
279 //this is because ASV does not implement the method SVGLocatable.getTransformToElement()
280 function getTransformToElement(node
,targetNode
) {
282 //this part is for fully conformant players
283 var CTM
= node
.getTransformToElement(targetNode
);
286 //this part is for ASV3 or other non-conformant players
287 // Initialize our CTM the node's Current Transformation Matrix
288 var CTM
= node
.getCTM();
289 // Work our way through the ancestor nodes stopping at the SVG Document
290 while ( ( node
= node
.parentNode
) != targetNode
) {
291 // Multiply the new CTM to the one with what we have accumulated so far
292 CTM
= node
.getCTM().multiply(CTM
);
298 //calculate HSV 2 RGB: HSV (h 0 to 360, sat and val are between 0 and 1), RGB between 0 and 255
299 function hsv2rgb(hue
,sat
,val
) {
301 var rgbArr
= new Array();
303 rgbArr
["red"] = Math
.round(val
* 255);
304 rgbArr
["green"] = Math
.round(val
* 255);
305 rgbArr
["blue"] = Math
.round(val
* 255);
309 var i
= Math
.floor(h
);
314 var m
= val
* (1 - sat
);
315 var n
= val
* (1 - sat
* f
);
324 rgbArr
["green"] = val
;
329 rgbArr
["green"] = val
;
335 rgbArr
["blue"] = val
;
340 rgbArr
["blue"] = val
;
353 rgbArr
["red"] = Math
.round(rgbArr
["red"] * 255);
354 rgbArr
["green"] = Math
.round(rgbArr
["green"] * 255);
355 rgbArr
["blue"] = Math
.round(rgbArr
["blue"] * 255);
360 //calculate rgb to hsv values
361 function rgb2hsv (red
,green
,blue
) {
362 //input between 0 and 255 --> normalize to 0 to 1
364 var hsvArr
= new Array();
368 myMax
= Math
.max(red
, Math
.max(green
,blue
));
369 myMin
= Math
.min(red
, Math
.min(green
,blue
));
372 s
= (myMax
- myMin
) / myMax
;
378 myDiff
= myMax
- myMin
;
379 rc
= (myMax
- red
) / myDiff
;
380 gc
= (myMax
- green
) / myDiff
;
381 bc
= (myMax
- blue
) / myDiff
;
385 if (green
== myMax
) {
386 h
= (2 + rc
- bc
) / 6;
389 h
= (4 + gc
- rc
) / 6;
398 hsvArr
["hue"] = Math
.round(h
* 360);
404 //populate an array that can be addressed by both a key or an index nr
405 function assArrayPopulate(arrayKeys
,arrayValues
) {
406 var returnArray
= new Array();
407 if (arrayKeys
.length
!= arrayValues
.length
) {
408 alert("error: arrays do not have same length!");
411 for (i
=0;i
<arrayKeys
.length
;i
++) {
412 returnArray
[arrayKeys
[i
]] = arrayValues
[i
];
418 //replace special (non-ASCII) characters with their charCode
419 function replaceSpecialChars(myString
) {
420 for (i
=161;i
<256;i
++) {
421 re
= new RegExp("&#"+i
+";","g");
422 myString
= myString
.replace(re
,String
.fromCharCode(i
));
427 /* ----------------------- getXMLData object ----------------------------- */
428 /* ----------------------------------------------------------------------- */
429 //this object allows to make network requests using getURL or XMLHttpRequest
430 //you may specify a url and a callBackFunction
431 //the callBackFunction receives a XML node representing the rootElement of the fragment received
432 function getXMLData(url
,callBackFunction
) {
434 this.callBackFunction
= callBackFunction
;
435 this.xmlRequest
= null;
438 getXMLData
.prototype.getData = function() {
439 //call getURL() if available
441 getURL(this.url
,this);
443 //or call XMLHttpRequest() if available
444 else if (window
.XMLHttpRequest
) {
446 this.xmlRequest
= new XMLHttpRequest();
447 this.xmlRequest
.overrideMimeType("text/xml");
448 this.xmlRequest
.open("GET",this.url
,true);
449 this.xmlRequest
.onreadystatechange = function() {_this
.handleEvent()};
450 this.xmlRequest
.send(null);
452 //write an error message if neither method is available
454 alert("your browser/svg viewer neither supports window.getURL nor window.XMLHttpRequest!");
458 //this is the callback method for the getURL function
459 getXMLData
.prototype.operationComplete = function(data
) {
460 //check if data has a success property
462 //parse content of the XML format to the variable "node"
463 var node
= parseXML(data
.content
,document
);
464 this.callBackFunction(node
.firstChild
);
467 alert("something went wrong with dynamic loading of geometry!");
471 //this method receives data from XMLHttpRequest
472 getXMLData
.prototype.handleEvent = function() {
473 if (this.xmlRequest
.readyState
== 4) {
474 var importedNode
= document
.importNode(this.xmlRequest
.responseXML
.documentElement
,true);
475 this.callBackFunction(importedNode
);
479 //starts an animation with the given id
480 //this function is useful in combination with window.setTimeout()
481 function startAnimation(id
) {
482 document
.getElementById(id
).beginElement();