SVG2.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. /* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the Clear BSD license.
  3. * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Renderer/NG.js
  7. */
  8. /**
  9. * Class: OpenLayers.Renderer.SVG2
  10. *
  11. * Inherits from:
  12. * - <OpenLayers.Renderer.NG>
  13. */
  14. OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, {
  15. /**
  16. * Property: xmlns
  17. * {String}
  18. */
  19. xmlns: "http://www.w3.org/2000/svg",
  20. /**
  21. * Property: xlinkns
  22. * {String}
  23. */
  24. xlinkns: "http://www.w3.org/1999/xlink",
  25. /**
  26. * Property: symbolMetrics
  27. * {Object} Cache for symbol metrics according to their svg coordinate
  28. * space. This is an object keyed by the symbol's id, and values are
  29. * an object with size, x and y properties.
  30. */
  31. symbolMetrics: null,
  32. /**
  33. * Constant: labelNodeType
  34. * {String} The node type for text label containers.
  35. */
  36. labelNodeType: "g",
  37. /**
  38. * Constructor: OpenLayers.Renderer.SVG2
  39. *
  40. * Parameters:
  41. * containerID - {String}
  42. */
  43. initialize: function(containerID) {
  44. if (!this.supported()) {
  45. return;
  46. }
  47. OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
  48. arguments);
  49. this.symbolMetrics = {};
  50. },
  51. /**
  52. * APIMethod: supported
  53. *
  54. * Returns:
  55. * {Boolean} Whether or not the browser supports the SVG renderer
  56. */
  57. supported: function() {
  58. var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
  59. return (document.implementation &&
  60. (document.implementation.hasFeature("org.w3c.svg", "1.0") ||
  61. document.implementation.hasFeature(svgFeature + "SVG", "1.1") ||
  62. document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
  63. },
  64. /**
  65. * Method: updateDimensions
  66. *
  67. * Parameters:
  68. * zoomChanged - {Boolean}
  69. */
  70. updateDimensions: function(zoomChanged) {
  71. OpenLayers.Renderer.NG.prototype.updateDimensions.apply(this, arguments);
  72. var res = this.getResolution();
  73. var width = this.extent.getWidth();
  74. var height = this.extent.getHeight();
  75. var extentString = [
  76. this.extent.left,
  77. -this.extent.top,
  78. width,
  79. height
  80. ].join(" ");
  81. this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
  82. this.rendererRoot.setAttributeNS(null, "width", width / res);
  83. this.rendererRoot.setAttributeNS(null, "height", height / res);
  84. if (zoomChanged === true) {
  85. // update styles for the new resolution
  86. var i, len;
  87. var nodes = this.vectorRoot.childNodes;
  88. for (i=0, len=nodes.length; i<len; ++i) {
  89. this.setStyle(nodes[i]);
  90. }
  91. var textNodes = this.textRoot.childNodes;
  92. var label;
  93. for (i=0, len=textNodes.length; i<len; ++i) {
  94. label = textNodes[i];
  95. this.drawText(label, label._style,
  96. new OpenLayers.Geometry.Point(label._x, label._y)
  97. );
  98. }
  99. }
  100. },
  101. /**
  102. * Method: getNodeType
  103. *
  104. * Parameters:
  105. * geometry - {<OpenLayers.Geometry>}
  106. * style - {Object}
  107. *
  108. * Returns:
  109. * {String} The corresponding node type for the specified geometry
  110. */
  111. getNodeType: function(geometry, style) {
  112. var nodeType = null;
  113. switch (geometry.CLASS_NAME) {
  114. case "OpenLayers.Geometry.Point":
  115. if (style.externalGraphic) {
  116. nodeType = "image";
  117. } else if (this.isComplexSymbol(style.graphicName)) {
  118. nodeType = "svg";
  119. } else {
  120. nodeType = "circle";
  121. }
  122. break;
  123. case "OpenLayers.Geometry.Rectangle":
  124. nodeType = "rect";
  125. break;
  126. case "OpenLayers.Geometry.LineString":
  127. nodeType = "polyline";
  128. break;
  129. case "OpenLayers.Geometry.LinearRing":
  130. nodeType = "polygon";
  131. break;
  132. case "OpenLayers.Geometry.Polygon":
  133. case "OpenLayers.Geometry.Curve":
  134. case "OpenLayers.Geometry.Surface":
  135. nodeType = "path";
  136. break;
  137. default:
  138. break;
  139. }
  140. return nodeType;
  141. },
  142. /**
  143. * Method: setStyle
  144. * Use to set all the style attributes to a SVG node.
  145. *
  146. * Takes care to adjust stroke width and point radius to be
  147. * resolution-relative
  148. *
  149. * Parameters:
  150. * node - {SVGDomElement} An SVG element to decorate
  151. * style - {Object}
  152. * options - {Object} Currently supported options include
  153. * 'isFilled' {Boolean} and
  154. * 'isStroked' {Boolean}
  155. */
  156. setStyle: function(node, style, options) {
  157. style = style || node._style;
  158. options = options || node._options;
  159. var resolution = this.getResolution();
  160. var r = node._radius;
  161. var widthFactor = resolution;
  162. if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
  163. node.style.visibility = "";
  164. if (style.graphic === false) {
  165. node.style.visibility = "hidden";
  166. } else if (style.externalGraphic) {
  167. if (style.graphicTitle) {
  168. node.setAttributeNS(null, "title", style.graphicTitle);
  169. //Standards-conformant SVG
  170. var label = this.nodeFactory(null, "title");
  171. label.textContent = style.graphicTitle;
  172. node.appendChild(label);
  173. }
  174. if (style.graphicWidth && style.graphicHeight) {
  175. node.setAttributeNS(null, "preserveAspectRatio", "none");
  176. }
  177. var width = style.graphicWidth || style.graphicHeight;
  178. var height = style.graphicHeight || style.graphicWidth;
  179. width = width ? width : style.pointRadius*2;
  180. height = height ? height : style.pointRadius*2;
  181. width *= resolution;
  182. height *= resolution;
  183. var xOffset = (style.graphicXOffset != undefined) ?
  184. style.graphicXOffset * resolution : -(0.5 * width);
  185. var yOffset = (style.graphicYOffset != undefined) ?
  186. style.graphicYOffset * resolution : -(0.5 * height);
  187. var opacity = style.graphicOpacity || style.fillOpacity;
  188. node.setAttributeNS(null, "x", node._x + xOffset);
  189. node.setAttributeNS(null, "y", node._y + yOffset);
  190. node.setAttributeNS(null, "width", width);
  191. node.setAttributeNS(null, "height", height);
  192. node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
  193. node.setAttributeNS(null, "style", "opacity: "+opacity);
  194. node.onclick = OpenLayers.Renderer.SVG2.preventDefault;
  195. } else if (this.isComplexSymbol(style.graphicName)) {
  196. // the symbol viewBox is three times as large as the symbol
  197. var offset = style.pointRadius * 3 * resolution;
  198. var size = offset * 2;
  199. var src = this.importSymbol(style.graphicName);
  200. widthFactor = this.symbolMetrics[src.id].size * 3 / size * resolution;
  201. // remove the node from the dom before we modify it. This
  202. // prevents various rendering issues in Safari and FF
  203. var parent = node.parentNode;
  204. var nextSibling = node.nextSibling;
  205. if(parent) {
  206. parent.removeChild(node);
  207. }
  208. // The more appropriate way to implement this would be use/defs,
  209. // but due to various issues in several browsers, it is safer to
  210. // copy the symbols instead of referencing them.
  211. // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985
  212. // and this email thread
  213. // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
  214. node.firstChild && node.removeChild(node.firstChild);
  215. node.appendChild(src.firstChild.cloneNode(true));
  216. node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
  217. node.setAttributeNS(null, "width", size);
  218. node.setAttributeNS(null, "height", size);
  219. node.setAttributeNS(null, "x", node._x - offset);
  220. node.setAttributeNS(null, "y", node._y - offset);
  221. // now that the node has all its new properties, insert it
  222. // back into the dom where it was
  223. if(nextSibling) {
  224. parent.insertBefore(node, nextSibling);
  225. } else if(parent) {
  226. parent.appendChild(node);
  227. }
  228. } else {
  229. node.setAttributeNS(null, "r", style.pointRadius * resolution);
  230. }
  231. var rotation = style.rotation;
  232. if (rotation !== undefined || node._rotation !== undefined) {
  233. node._rotation = rotation;
  234. rotation |= 0;
  235. if (node.nodeName !== "svg") {
  236. node.setAttributeNS(null, "transform",
  237. ["rotate(", rotation, node._x, node._y, ")"].join(" ")
  238. );
  239. } else {
  240. var metrics = this.symbolMetrics[src.id];
  241. node.firstChild.setAttributeNS(null, "transform",
  242. ["rotate(", rotation, metrics.x, metrics.y, ")"].join(" ")
  243. );
  244. }
  245. }
  246. }
  247. if (options.isFilled) {
  248. node.setAttributeNS(null, "fill", style.fillColor);
  249. node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
  250. } else {
  251. node.setAttributeNS(null, "fill", "none");
  252. }
  253. if (options.isStroked) {
  254. node.setAttributeNS(null, "stroke", style.strokeColor);
  255. node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
  256. node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
  257. node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
  258. // Hard-coded linejoin for now, to make it look the same as in VML.
  259. // There is no strokeLinejoin property yet for symbolizers.
  260. node.setAttributeNS(null, "stroke-linejoin", "round");
  261. style.strokeDashstyle && node.setAttributeNS(null,
  262. "stroke-dasharray", this.dashStyle(style, widthFactor));
  263. } else {
  264. node.setAttributeNS(null, "stroke", "none");
  265. }
  266. if (style.pointerEvents) {
  267. node.setAttributeNS(null, "pointer-events", style.pointerEvents);
  268. }
  269. if (style.cursor != null) {
  270. node.setAttributeNS(null, "cursor", style.cursor);
  271. }
  272. return node;
  273. },
  274. /**
  275. * Method: dashStyle
  276. *
  277. * Parameters:
  278. * style - {Object}
  279. * widthFactor - {Number}
  280. *
  281. * Returns:
  282. * {String} A SVG compliant 'stroke-dasharray' value
  283. */
  284. dashStyle: function(style, widthFactor) {
  285. var w = style.strokeWidth * widthFactor;
  286. var str = style.strokeDashstyle;
  287. switch (str) {
  288. case 'solid':
  289. return 'none';
  290. case 'dot':
  291. return [widthFactor, 4 * w].join();
  292. case 'dash':
  293. return [4 * w, 4 * w].join();
  294. case 'dashdot':
  295. return [4 * w, 4 * w, widthFactor, 4 * w].join();
  296. case 'longdash':
  297. return [8 * w, 4 * w].join();
  298. case 'longdashdot':
  299. return [8 * w, 4 * w, widthFactor, 4 * w].join();
  300. default:
  301. var parts = OpenLayers.String.trim(str).split(/\s+/g);
  302. for (var i=0, ii=parts.length; i<ii; i++) {
  303. parts[i] = parts[i] * widthFactor;
  304. }
  305. return parts.join();
  306. }
  307. },
  308. /**
  309. * Method: createNode
  310. *
  311. * Parameters:
  312. * type - {String} Kind of node to draw
  313. * id - {String} Id for node
  314. *
  315. * Returns:
  316. * {DOMElement} A new node of the given type and id
  317. */
  318. createNode: function(type, id) {
  319. var node = document.createElementNS(this.xmlns, type);
  320. if (id) {
  321. node.setAttributeNS(null, "id", id);
  322. }
  323. return node;
  324. },
  325. /**
  326. * Method: nodeTypeCompare
  327. *
  328. * Parameters:
  329. * node - {SVGDomElement} An SVG element
  330. * type - {String} Kind of node
  331. *
  332. * Returns:
  333. * {Boolean} Whether or not the specified node is of the specified type
  334. */
  335. nodeTypeCompare: function(node, type) {
  336. return (type == node.nodeName);
  337. },
  338. /**
  339. * Method: createRenderRoot
  340. *
  341. * Returns:
  342. * {DOMElement} The specific render engine's root element
  343. */
  344. createRenderRoot: function() {
  345. return this.nodeFactory(this.container.id + "_svgRoot", "svg");
  346. },
  347. /**
  348. * Method: createRoot
  349. *
  350. * Parameter:
  351. * suffix - {String} suffix to append to the id
  352. *
  353. * Returns:
  354. * {DOMElement}
  355. */
  356. createRoot: function(suffix) {
  357. return this.nodeFactory(this.container.id + suffix, "g");
  358. },
  359. /**
  360. * Method: createDefs
  361. *
  362. * Returns:
  363. * {DOMElement} The element to which we'll add the symbol definitions
  364. */
  365. createDefs: function() {
  366. var defs = this.nodeFactory(this.container.id + "_defs", "defs");
  367. this.rendererRoot.appendChild(defs);
  368. return defs;
  369. },
  370. /**************************************
  371. * *
  372. * GEOMETRY DRAWING FUNCTIONS *
  373. * *
  374. **************************************/
  375. /**
  376. * Method: drawPoint
  377. * This method is only called by the renderer itself.
  378. *
  379. * Parameters:
  380. * node - {DOMElement}
  381. * geometry - {<OpenLayers.Geometry>}
  382. *
  383. * Returns:
  384. * {DOMElement} or false if the renderer could not draw the point
  385. */
  386. drawPoint: function(node, geometry) {
  387. return this.drawCircle(node, geometry, 1);
  388. },
  389. /**
  390. * Method: drawCircle
  391. * This method is only called by the renderer itself.
  392. *
  393. * Parameters:
  394. * node - {DOMElement}
  395. * geometry - {<OpenLayers.Geometry>}
  396. * radius - {Float}
  397. *
  398. * Returns:
  399. * {DOMElement} or false if the renderer could not draw the circle
  400. */
  401. drawCircle: function(node, geometry, radius) {
  402. var x = geometry.x;
  403. var y = -geometry.y;
  404. node.setAttributeNS(null, "cx", x);
  405. node.setAttributeNS(null, "cy", y);
  406. node._x = x;
  407. node._y = y;
  408. node._radius = radius;
  409. return node;
  410. },
  411. /**
  412. * Method: drawLineString
  413. * This method is only called by the renderer itself.
  414. *
  415. * Parameters:
  416. * node - {DOMElement}
  417. * geometry - {<OpenLayers.Geometry>}
  418. *
  419. * Returns:
  420. * {DOMElement} or null if the renderer could not draw all components of
  421. * the linestring, or false if nothing could be drawn
  422. */
  423. drawLineString: function(node, geometry) {
  424. var path = this.getComponentsString(geometry.components);
  425. node.setAttributeNS(null, "points", path);
  426. return node;
  427. },
  428. /**
  429. * Method: drawLinearRing
  430. * This method is only called by the renderer itself.
  431. *
  432. * Parameters:
  433. * node - {DOMElement}
  434. * geometry - {<OpenLayers.Geometry>}
  435. *
  436. * Returns:
  437. * {DOMElement} or null if the renderer could not draw all components
  438. * of the linear ring, or false if nothing could be drawn
  439. */
  440. drawLinearRing: function(node, geometry) {
  441. var path = this.getComponentsString(geometry.components);
  442. node.setAttributeNS(null, "points", path);
  443. return node;
  444. },
  445. /**
  446. * Method: drawPolygon
  447. * This method is only called by the renderer itself.
  448. *
  449. * Parameters:
  450. * node - {DOMElement}
  451. * geometry - {<OpenLayers.Geometry>}
  452. *
  453. * Returns:
  454. * {DOMElement} or null if the renderer could not draw all components
  455. * of the polygon, or false if nothing could be drawn
  456. */
  457. drawPolygon: function(node, geometry) {
  458. var d = [];
  459. var draw = true;
  460. var complete = true;
  461. var linearRingResult, path;
  462. for (var j=0, len=geometry.components.length; j<len; j++) {
  463. d.push("M");
  464. path = this.getComponentsString(
  465. geometry.components[j].components, " ");
  466. d.push(path);
  467. }
  468. d.push("z");
  469. node.setAttributeNS(null, "d", d.join(" "));
  470. node.setAttributeNS(null, "fill-rule", "evenodd");
  471. return node;
  472. },
  473. /**
  474. * Method: drawRectangle
  475. * This method is only called by the renderer itself.
  476. *
  477. * Parameters:
  478. * node - {DOMElement}
  479. * geometry - {<OpenLayers.Geometry>}
  480. *
  481. * Returns:
  482. * {DOMElement} or false if the renderer could not draw the rectangle
  483. */
  484. drawRectangle: function(node, geometry) {
  485. node.setAttributeNS(null, "x", geometry.x);
  486. node.setAttributeNS(null, "y", -geometry.y);
  487. node.setAttributeNS(null, "width", geometry.width);
  488. node.setAttributeNS(null, "height", geometry.height);
  489. return node;
  490. },
  491. /**
  492. * Method: drawSurface
  493. * This method is only called by the renderer itself.
  494. *
  495. * Parameters:
  496. * node - {DOMElement}
  497. * geometry - {<OpenLayers.Geometry>}
  498. *
  499. * Returns:
  500. * {DOMElement} or false if the renderer could not draw the surface
  501. */
  502. drawSurface: function(node, geometry) {
  503. // create the svg path string representation
  504. var d = [];
  505. var draw = true;
  506. for (var i=0, len=geometry.components.length; i<len; i++) {
  507. if ((i%3) == 0 && (i/3) == 0) {
  508. var component = this.getShortString(geometry.components[i]);
  509. d.push("M", component);
  510. } else if ((i%3) == 1) {
  511. var component = this.getShortString(geometry.components[i]);
  512. d.push("C", component);
  513. } else {
  514. var component = this.getShortString(geometry.components[i]);
  515. d.push(component);
  516. }
  517. }
  518. d.push("Z");
  519. node.setAttributeNS(null, "d", d.join(" "));
  520. return node;
  521. },
  522. /**
  523. * Method: drawText
  524. * Function for drawing text labels.
  525. * This method is only called by the renderer itself.
  526. *
  527. * Parameters:
  528. * featureId - {String|DOMElement}
  529. * style - {Object}
  530. * location - {<OpenLayers.Geometry.Point>}, will be modified inline
  531. *
  532. * Returns:
  533. * {DOMElement} container holding the text label
  534. */
  535. drawText: function(featureId, style, location) {
  536. var g = OpenLayers.Renderer.NG.prototype.drawText.apply(this, arguments);
  537. var text = g.firstChild ||
  538. this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_text", "text");
  539. var res = this.getResolution();
  540. text.setAttributeNS(null, "x", location.x / res);
  541. text.setAttributeNS(null, "y", - location.y / res);
  542. g.setAttributeNS(null, "transform", "scale(" + res + ")");
  543. if (style.fontColor) {
  544. text.setAttributeNS(null, "fill", style.fontColor);
  545. }
  546. if (style.fontOpacity) {
  547. text.setAttributeNS(null, "opacity", style.fontOpacity);
  548. }
  549. if (style.fontFamily) {
  550. text.setAttributeNS(null, "font-family", style.fontFamily);
  551. }
  552. if (style.fontSize) {
  553. text.setAttributeNS(null, "font-size", style.fontSize);
  554. }
  555. if (style.fontWeight) {
  556. text.setAttributeNS(null, "font-weight", style.fontWeight);
  557. }
  558. if (style.fontStyle) {
  559. text.setAttributeNS(null, "font-style", style.fontStyle);
  560. }
  561. if (style.labelSelect === true) {
  562. text.setAttributeNS(null, "pointer-events", "visible");
  563. text._featureId = featureId;
  564. } else {
  565. text.setAttributeNS(null, "pointer-events", "none");
  566. }
  567. var align = style.labelAlign || "cm";
  568. text.setAttributeNS(null, "text-anchor",
  569. OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[0]] || "middle");
  570. if (OpenLayers.IS_GECKO === true) {
  571. text.setAttributeNS(null, "dominant-baseline",
  572. OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[1]] || "central");
  573. }
  574. var labelRows = style.label.split('\n');
  575. var numRows = labelRows.length;
  576. while (text.childNodes.length > numRows) {
  577. text.removeChild(text.lastChild);
  578. }
  579. for (var i = 0; i < numRows; i++) {
  580. var tspan = text.childNodes[i] ||
  581. this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan");
  582. if (style.labelSelect === true) {
  583. tspan._featureId = featureId;
  584. }
  585. if (OpenLayers.IS_GECKO === false) {
  586. tspan.setAttributeNS(null, "baseline-shift",
  587. OpenLayers.Renderer.SVG2.LABEL_VSHIFT[align[1]] || "-35%");
  588. }
  589. tspan.setAttribute("x", location.x / res);
  590. if (i == 0) {
  591. var vfactor = OpenLayers.Renderer.SVG2.LABEL_VFACTOR[align[1]];
  592. if (vfactor == null) {
  593. vfactor = -.5;
  594. }
  595. tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
  596. } else {
  597. tspan.setAttribute("dy", "1em");
  598. }
  599. tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
  600. if (!tspan.parentNode) {
  601. text.appendChild(tspan);
  602. }
  603. }
  604. if (!text.parentNode) {
  605. g.appendChild(text);
  606. }
  607. return g;
  608. },
  609. /**
  610. * Method: getComponentString
  611. *
  612. * Parameters:
  613. * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
  614. * separator - {String} character between coordinate pairs. Defaults to ","
  615. *
  616. * Returns:
  617. * {Object} hash with properties "path" (the string created from the
  618. * components and "complete" (false if the renderer was unable to
  619. * draw all components)
  620. */
  621. getComponentsString: function(components, separator) {
  622. var len = components.length;
  623. var strings = new Array(len);
  624. for (var i=0; i<len; i++) {
  625. strings[i] = this.getShortString(components[i]);
  626. }
  627. return strings.join(separator || ",");
  628. },
  629. /**
  630. * Method: getShortString
  631. *
  632. * Parameters:
  633. * point - {<OpenLayers.Geometry.Point>}
  634. *
  635. * Returns:
  636. * {String} or false if point is outside the valid range
  637. */
  638. getShortString: function(point) {
  639. return point.x + "," + (-point.y);
  640. },
  641. /**
  642. * Method: importSymbol
  643. * add a new symbol definition from the rendererer's symbol hash
  644. *
  645. * Parameters:
  646. * graphicName - {String} name of the symbol to import
  647. *
  648. * Returns:
  649. * {DOMElement} - the imported symbol
  650. */
  651. importSymbol: function (graphicName) {
  652. if (!this.defs) {
  653. // create svg defs tag
  654. this.defs = this.createDefs();
  655. }
  656. var id = this.container.id + "-" + graphicName;
  657. // check if symbol already exists in the defs
  658. var existing = document.getElementById(id);
  659. if (existing != null) {
  660. return existing;
  661. }
  662. var symbol = OpenLayers.Renderer.symbol[graphicName];
  663. if (!symbol) {
  664. throw new Error(graphicName + ' is not a valid symbol name');
  665. }
  666. var symbolNode = this.nodeFactory(id, "symbol");
  667. var node = this.nodeFactory(null, "polygon");
  668. symbolNode.appendChild(node);
  669. var symbolExtent = new OpenLayers.Bounds(
  670. Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
  671. var points = [];
  672. var x,y;
  673. for (var i=0, len=symbol.length; i<len; i=i+2) {
  674. x = symbol[i];
  675. y = symbol[i+1];
  676. symbolExtent.left = Math.min(symbolExtent.left, x);
  677. symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
  678. symbolExtent.right = Math.max(symbolExtent.right, x);
  679. symbolExtent.top = Math.max(symbolExtent.top, y);
  680. points.push(x, ",", y);
  681. }
  682. node.setAttributeNS(null, "points", points.join(" "));
  683. var width = symbolExtent.getWidth();
  684. var height = symbolExtent.getHeight();
  685. // create a viewBox three times as large as the symbol itself,
  686. // to allow for strokeWidth being displayed correctly at the corners.
  687. var viewBox = [symbolExtent.left - width,
  688. symbolExtent.bottom - height, width * 3, height * 3];
  689. symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
  690. this.symbolMetrics[id] = {
  691. size: Math.max(width, height),
  692. x: symbolExtent.getCenterLonLat().lon,
  693. y: symbolExtent.getCenterLonLat().lat
  694. };
  695. this.defs.appendChild(symbolNode);
  696. return symbolNode;
  697. },
  698. /**
  699. * Method: getFeatureIdFromEvent
  700. *
  701. * Parameters:
  702. * evt - {Object} An <OpenLayers.Event> object
  703. *
  704. * Returns:
  705. * {<OpenLayers.Geometry>} A geometry from an event that
  706. * happened on a layer.
  707. */
  708. getFeatureIdFromEvent: function(evt) {
  709. var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
  710. if(!featureId) {
  711. var target = evt.target;
  712. featureId = target.parentNode && target != this.rendererRoot &&
  713. target.parentNode._featureId;
  714. }
  715. return featureId;
  716. },
  717. CLASS_NAME: "OpenLayers.Renderer.SVG2"
  718. });
  719. /**
  720. * Constant: OpenLayers.Renderer.SVG2.LABEL_ALIGN
  721. * {Object}
  722. */
  723. OpenLayers.Renderer.SVG2.LABEL_ALIGN = {
  724. "l": "start",
  725. "r": "end",
  726. "b": "bottom",
  727. "t": "hanging"
  728. };
  729. /**
  730. * Constant: OpenLayers.Renderer.SVG2.LABEL_VSHIFT
  731. * {Object}
  732. */
  733. OpenLayers.Renderer.SVG2.LABEL_VSHIFT = {
  734. // according to
  735. // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
  736. // a baseline-shift of -70% shifts the text exactly from the
  737. // bottom to the top of the baseline, so -35% moves the text to
  738. // the center of the baseline.
  739. "t": "-70%",
  740. "b": "0"
  741. };
  742. /**
  743. * Constant: OpenLayers.Renderer.SVG2.LABEL_VFACTOR
  744. * {Object}
  745. */
  746. OpenLayers.Renderer.SVG2.LABEL_VFACTOR = {
  747. "t": 0,
  748. "b": -1
  749. };
  750. /**
  751. * Function: OpenLayers.Renderer.SVG2.preventDefault
  752. * Used to prevent default events (especially opening images in a new tab on
  753. * ctrl-click) from being executed for externalGraphic and graphicName symbols
  754. */
  755. OpenLayers.Renderer.SVG2.preventDefault = function(e) {
  756. e.preventDefault && e.preventDefault();
  757. };