Elements.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017
  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.js
  7. */
  8. /**
  9. * Class: OpenLayers.ElementsIndexer
  10. * This class takes care of figuring out which order elements should be
  11. * placed in the DOM based on given indexing methods.
  12. */
  13. OpenLayers.ElementsIndexer = OpenLayers.Class({
  14. /**
  15. * Property: maxZIndex
  16. * {Integer} This is the largest-most z-index value for a node
  17. * contained within the indexer.
  18. */
  19. maxZIndex: null,
  20. /**
  21. * Property: order
  22. * {Array<String>} This is an array of node id's stored in the
  23. * order that they should show up on screen. Id's higher up in the
  24. * array (higher array index) represent nodes with higher z-indeces.
  25. */
  26. order: null,
  27. /**
  28. * Property: indices
  29. * {Object} This is a hash that maps node ids to their z-index value
  30. * stored in the indexer. This is done to make finding a nodes z-index
  31. * value O(1).
  32. */
  33. indices: null,
  34. /**
  35. * Property: compare
  36. * {Function} This is the function used to determine placement of
  37. * of a new node within the indexer. If null, this defaults to to
  38. * the Z_ORDER_DRAWING_ORDER comparison method.
  39. */
  40. compare: null,
  41. /**
  42. * APIMethod: initialize
  43. * Create a new indexer with
  44. *
  45. * Parameters:
  46. * yOrdering - {Boolean} Whether to use y-ordering.
  47. */
  48. initialize: function(yOrdering) {
  49. this.compare = yOrdering ?
  50. OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
  51. OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
  52. this.clear();
  53. },
  54. /**
  55. * APIMethod: insert
  56. * Insert a new node into the indexer. In order to find the correct
  57. * positioning for the node to be inserted, this method uses a binary
  58. * search. This makes inserting O(log(n)).
  59. *
  60. * Parameters:
  61. * newNode - {DOMElement} The new node to be inserted.
  62. *
  63. * Returns
  64. * {DOMElement} the node before which we should insert our newNode, or
  65. * null if newNode can just be appended.
  66. */
  67. insert: function(newNode) {
  68. // If the node is known to the indexer, remove it so we can
  69. // recalculate where it should go.
  70. if (this.exists(newNode)) {
  71. this.remove(newNode);
  72. }
  73. var nodeId = newNode.id;
  74. this.determineZIndex(newNode);
  75. var leftIndex = -1;
  76. var rightIndex = this.order.length;
  77. var middle;
  78. while (rightIndex - leftIndex > 1) {
  79. middle = parseInt((leftIndex + rightIndex) / 2);
  80. var placement = this.compare(this, newNode,
  81. OpenLayers.Util.getElement(this.order[middle]));
  82. if (placement > 0) {
  83. leftIndex = middle;
  84. } else {
  85. rightIndex = middle;
  86. }
  87. }
  88. this.order.splice(rightIndex, 0, nodeId);
  89. this.indices[nodeId] = this.getZIndex(newNode);
  90. // If the new node should be before another in the index
  91. // order, return the node before which we have to insert the new one;
  92. // else, return null to indicate that the new node can be appended.
  93. return this.getNextElement(rightIndex);
  94. },
  95. /**
  96. * APIMethod: remove
  97. *
  98. * Parameters:
  99. * node - {DOMElement} The node to be removed.
  100. */
  101. remove: function(node) {
  102. var nodeId = node.id;
  103. var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
  104. if (arrayIndex >= 0) {
  105. // Remove it from the order array, as well as deleting the node
  106. // from the indeces hash.
  107. this.order.splice(arrayIndex, 1);
  108. delete this.indices[nodeId];
  109. // Reset the maxium z-index based on the last item in the
  110. // order array.
  111. if (this.order.length > 0) {
  112. var lastId = this.order[this.order.length - 1];
  113. this.maxZIndex = this.indices[lastId];
  114. } else {
  115. this.maxZIndex = 0;
  116. }
  117. }
  118. },
  119. /**
  120. * APIMethod: clear
  121. */
  122. clear: function() {
  123. this.order = [];
  124. this.indices = {};
  125. this.maxZIndex = 0;
  126. },
  127. /**
  128. * APIMethod: exists
  129. *
  130. * Parameters:
  131. * node- {DOMElement} The node to test for existence.
  132. *
  133. * Returns:
  134. * {Boolean} Whether or not the node exists in the indexer?
  135. */
  136. exists: function(node) {
  137. return (this.indices[node.id] != null);
  138. },
  139. /**
  140. * APIMethod: getZIndex
  141. * Get the z-index value for the current node from the node data itself.
  142. *
  143. * Parameters:
  144. * node - {DOMElement} The node whose z-index to get.
  145. *
  146. * Returns:
  147. * {Integer} The z-index value for the specified node (from the node
  148. * data itself).
  149. */
  150. getZIndex: function(node) {
  151. return node._style.graphicZIndex;
  152. },
  153. /**
  154. * Method: determineZIndex
  155. * Determine the z-index for the current node if there isn't one,
  156. * and set the maximum value if we've found a new maximum.
  157. *
  158. * Parameters:
  159. * node - {DOMElement}
  160. */
  161. determineZIndex: function(node) {
  162. var zIndex = node._style.graphicZIndex;
  163. // Everything must have a zIndex. If none is specified,
  164. // this means the user *must* (hint: assumption) want this
  165. // node to succomb to drawing order. To enforce drawing order
  166. // over all indexing methods, we'll create a new z-index that's
  167. // greater than any currently in the indexer.
  168. if (zIndex == null) {
  169. zIndex = this.maxZIndex;
  170. node._style.graphicZIndex = zIndex;
  171. } else if (zIndex > this.maxZIndex) {
  172. this.maxZIndex = zIndex;
  173. }
  174. },
  175. /**
  176. * APIMethod: getNextElement
  177. * Get the next element in the order stack.
  178. *
  179. * Parameters:
  180. * index - {Integer} The index of the current node in this.order.
  181. *
  182. * Returns:
  183. * {DOMElement} the node following the index passed in, or
  184. * null.
  185. */
  186. getNextElement: function(index) {
  187. var nextIndex = index + 1;
  188. if (nextIndex < this.order.length) {
  189. var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
  190. if (nextElement == undefined) {
  191. nextElement = this.getNextElement(nextIndex);
  192. }
  193. return nextElement;
  194. } else {
  195. return null;
  196. }
  197. },
  198. CLASS_NAME: "OpenLayers.ElementsIndexer"
  199. });
  200. /**
  201. * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
  202. * These are the compare methods for figuring out where a new node should be
  203. * placed within the indexer. These methods are very similar to general
  204. * sorting methods in that they return -1, 0, and 1 to specify the
  205. * direction in which new nodes fall in the ordering.
  206. */
  207. OpenLayers.ElementsIndexer.IndexingMethods = {
  208. /**
  209. * Method: Z_ORDER
  210. * This compare method is used by other comparison methods.
  211. * It can be used individually for ordering, but is not recommended,
  212. * because it doesn't subscribe to drawing order.
  213. *
  214. * Parameters:
  215. * indexer - {<OpenLayers.ElementsIndexer>}
  216. * newNode - {DOMElement}
  217. * nextNode - {DOMElement}
  218. *
  219. * Returns:
  220. * {Integer}
  221. */
  222. Z_ORDER: function(indexer, newNode, nextNode) {
  223. var newZIndex = indexer.getZIndex(newNode);
  224. var returnVal = 0;
  225. if (nextNode) {
  226. var nextZIndex = indexer.getZIndex(nextNode);
  227. returnVal = newZIndex - nextZIndex;
  228. }
  229. return returnVal;
  230. },
  231. /**
  232. * APIMethod: Z_ORDER_DRAWING_ORDER
  233. * This method orders nodes by their z-index, but does so in a way
  234. * that, if there are other nodes with the same z-index, the newest
  235. * drawn will be the front most within that z-index. This is the
  236. * default indexing method.
  237. *
  238. * Parameters:
  239. * indexer - {<OpenLayers.ElementsIndexer>}
  240. * newNode - {DOMElement}
  241. * nextNode - {DOMElement}
  242. *
  243. * Returns:
  244. * {Integer}
  245. */
  246. Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
  247. var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
  248. indexer,
  249. newNode,
  250. nextNode
  251. );
  252. // Make Z_ORDER subscribe to drawing order by pushing it above
  253. // all of the other nodes with the same z-index.
  254. if (nextNode && returnVal == 0) {
  255. returnVal = 1;
  256. }
  257. return returnVal;
  258. },
  259. /**
  260. * APIMethod: Z_ORDER_Y_ORDER
  261. * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
  262. * best describes which ordering methods have precedence (though, the
  263. * name would be too long). This method orders nodes by their z-index,
  264. * but does so in a way that, if there are other nodes with the same
  265. * z-index, the nodes with the lower y position will be "closer" than
  266. * those with a higher y position. If two nodes have the exact same y
  267. * position, however, then this method will revert to using drawing
  268. * order to decide placement.
  269. *
  270. * Parameters:
  271. * indexer - {<OpenLayers.ElementsIndexer>}
  272. * newNode - {DOMElement}
  273. * nextNode - {DOMElement}
  274. *
  275. * Returns:
  276. * {Integer}
  277. */
  278. Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
  279. var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
  280. indexer,
  281. newNode,
  282. nextNode
  283. );
  284. if (nextNode && returnVal === 0) {
  285. var result = nextNode._boundsBottom - newNode._boundsBottom;
  286. returnVal = (result === 0) ? 1 : result;
  287. }
  288. return returnVal;
  289. }
  290. };
  291. /**
  292. * Class: OpenLayers.Renderer.Elements
  293. * This is another virtual class in that it should never be instantiated by
  294. * itself as a Renderer. It exists because there is *tons* of shared
  295. * functionality between different vector libraries which use nodes/elements
  296. * as a base for rendering vectors.
  297. *
  298. * The highlevel bits of code that are implemented here are the adding and
  299. * removing of geometries, which is essentially the same for any
  300. * element-based renderer. The details of creating each node and drawing the
  301. * paths are of course different, but the machinery is the same.
  302. *
  303. * Inherits:
  304. * - <OpenLayers.Renderer>
  305. */
  306. OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
  307. /**
  308. * Property: rendererRoot
  309. * {DOMElement}
  310. */
  311. rendererRoot: null,
  312. /**
  313. * Property: root
  314. * {DOMElement}
  315. */
  316. root: null,
  317. /**
  318. * Property: vectorRoot
  319. * {DOMElement}
  320. */
  321. vectorRoot: null,
  322. /**
  323. * Property: textRoot
  324. * {DOMElement}
  325. */
  326. textRoot: null,
  327. /**
  328. * Property: xmlns
  329. * {String}
  330. */
  331. xmlns: null,
  332. /**
  333. * Property: Indexer
  334. * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer
  335. * created upon initialization if the zIndexing or yOrdering options
  336. * passed to this renderer's constructor are set to true.
  337. */
  338. indexer: null,
  339. /**
  340. * Constant: BACKGROUND_ID_SUFFIX
  341. * {String}
  342. */
  343. BACKGROUND_ID_SUFFIX: "_background",
  344. /**
  345. * Constant: LABEL_ID_SUFFIX
  346. * {String}
  347. */
  348. LABEL_ID_SUFFIX: "_label",
  349. /**
  350. * Constructor: OpenLayers.Renderer.Elements
  351. *
  352. * Parameters:
  353. * containerID - {String}
  354. * options - {Object} options for this renderer. Supported options are:
  355. * * yOrdering - {Boolean} Whether to use y-ordering
  356. * * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
  357. * if yOrdering is set to true.
  358. */
  359. initialize: function(containerID, options) {
  360. OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
  361. this.rendererRoot = this.createRenderRoot();
  362. this.root = this.createRoot("_root");
  363. this.vectorRoot = this.createRoot("_vroot");
  364. this.textRoot = this.createRoot("_troot");
  365. this.root.appendChild(this.vectorRoot);
  366. this.root.appendChild(this.textRoot);
  367. this.rendererRoot.appendChild(this.root);
  368. this.container.appendChild(this.rendererRoot);
  369. if(options && (options.zIndexing || options.yOrdering)) {
  370. this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
  371. }
  372. },
  373. /**
  374. * Method: destroy
  375. */
  376. destroy: function() {
  377. this.clear();
  378. this.rendererRoot = null;
  379. this.root = null;
  380. this.xmlns = null;
  381. OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
  382. },
  383. /**
  384. * Method: clear
  385. * Remove all the elements from the root
  386. */
  387. clear: function() {
  388. var child;
  389. var root = this.vectorRoot;
  390. if (root) {
  391. while (child = root.firstChild) {
  392. root.removeChild(child);
  393. }
  394. }
  395. root = this.textRoot;
  396. if (root) {
  397. while (child = root.firstChild) {
  398. root.removeChild(child);
  399. }
  400. }
  401. if (this.indexer) {
  402. this.indexer.clear();
  403. }
  404. },
  405. /**
  406. * Method: getNodeType
  407. * This function is in charge of asking the specific renderer which type
  408. * of node to create for the given geometry and style. All geometries
  409. * in an Elements-based renderer consist of one node and some
  410. * attributes. We have the nodeFactory() function which creates a node
  411. * for us, but it takes a 'type' as input, and that is precisely what
  412. * this function tells us.
  413. *
  414. * Parameters:
  415. * geometry - {<OpenLayers.Geometry>}
  416. * style - {Object}
  417. *
  418. * Returns:
  419. * {String} The corresponding node type for the specified geometry
  420. */
  421. getNodeType: function(geometry, style) { },
  422. /**
  423. * Method: drawGeometry
  424. * Draw the geometry, creating new nodes, setting paths, setting style,
  425. * setting featureId on the node. This method should only be called
  426. * by the renderer itself.
  427. *
  428. * Parameters:
  429. * geometry - {<OpenLayers.Geometry>}
  430. * style - {Object}
  431. * featureId - {String}
  432. *
  433. * Returns:
  434. * {Boolean} true if the geometry has been drawn completely; null if
  435. * incomplete; false otherwise
  436. */
  437. drawGeometry: function(geometry, style, featureId) {
  438. var className = geometry.CLASS_NAME;
  439. var rendered = true;
  440. if ((className == "OpenLayers.Geometry.Collection") ||
  441. (className == "OpenLayers.Geometry.MultiPoint") ||
  442. (className == "OpenLayers.Geometry.MultiLineString") ||
  443. (className == "OpenLayers.Geometry.MultiPolygon")) {
  444. for (var i = 0, len=geometry.components.length; i<len; i++) {
  445. rendered = this.drawGeometry(
  446. geometry.components[i], style, featureId) && rendered;
  447. }
  448. return rendered;
  449. };
  450. rendered = false;
  451. var removeBackground = false;
  452. if (style.display != "none") {
  453. if (style.backgroundGraphic) {
  454. this.redrawBackgroundNode(geometry.id, geometry, style,
  455. featureId);
  456. } else {
  457. removeBackground = true;
  458. }
  459. rendered = this.redrawNode(geometry.id, geometry, style,
  460. featureId);
  461. }
  462. if (rendered == false) {
  463. var node = document.getElementById(geometry.id);
  464. if (node) {
  465. if (node._style.backgroundGraphic) {
  466. removeBackground = true;
  467. }
  468. node.parentNode.removeChild(node);
  469. }
  470. }
  471. if (removeBackground) {
  472. var node = document.getElementById(
  473. geometry.id + this.BACKGROUND_ID_SUFFIX);
  474. if (node) {
  475. node.parentNode.removeChild(node);
  476. }
  477. }
  478. return rendered;
  479. },
  480. /**
  481. * Method: redrawNode
  482. *
  483. * Parameters:
  484. * id - {String}
  485. * geometry - {<OpenLayers.Geometry>}
  486. * style - {Object}
  487. * featureId - {String}
  488. *
  489. * Returns:
  490. * {Boolean} true if the complete geometry could be drawn, null if parts of
  491. * the geometry could not be drawn, false otherwise
  492. */
  493. redrawNode: function(id, geometry, style, featureId) {
  494. style = this.applyDefaultSymbolizer(style);
  495. // Get the node if it's already on the map.
  496. var node = this.nodeFactory(id, this.getNodeType(geometry, style));
  497. // Set the data for the node, then draw it.
  498. node._featureId = featureId;
  499. node._boundsBottom = geometry.getBounds().bottom;
  500. node._geometryClass = geometry.CLASS_NAME;
  501. node._style = style;
  502. var drawResult = this.drawGeometryNode(node, geometry, style);
  503. if(drawResult === false) {
  504. return false;
  505. }
  506. node = drawResult.node;
  507. // Insert the node into the indexer so it can show us where to
  508. // place it. Note that this operation is O(log(n)). If there's a
  509. // performance problem (when dragging, for instance) this is
  510. // likely where it would be.
  511. if (this.indexer) {
  512. var insert = this.indexer.insert(node);
  513. if (insert) {
  514. this.vectorRoot.insertBefore(node, insert);
  515. } else {
  516. this.vectorRoot.appendChild(node);
  517. }
  518. } else {
  519. // if there's no indexer, simply append the node to root,
  520. // but only if the node is a new one
  521. if (node.parentNode !== this.vectorRoot){
  522. this.vectorRoot.appendChild(node);
  523. }
  524. }
  525. this.postDraw(node);
  526. return drawResult.complete;
  527. },
  528. /**
  529. * Method: redrawBackgroundNode
  530. * Redraws the node using special 'background' style properties. Basically
  531. * just calls redrawNode(), but instead of directly using the
  532. * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and
  533. * 'graphicZIndex' properties directly from the specified 'style'
  534. * parameter, we create a new style object and set those properties
  535. * from the corresponding 'background'-prefixed properties from
  536. * specified 'style' parameter.
  537. *
  538. * Parameters:
  539. * id - {String}
  540. * geometry - {<OpenLayers.Geometry>}
  541. * style - {Object}
  542. * featureId - {String}
  543. *
  544. * Returns:
  545. * {Boolean} true if the complete geometry could be drawn, null if parts of
  546. * the geometry could not be drawn, false otherwise
  547. */
  548. redrawBackgroundNode: function(id, geometry, style, featureId) {
  549. var backgroundStyle = OpenLayers.Util.extend({}, style);
  550. // Set regular style attributes to apply to the background styles.
  551. backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
  552. backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
  553. backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
  554. backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
  555. backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
  556. backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
  557. // Erase background styles.
  558. backgroundStyle.backgroundGraphic = null;
  559. backgroundStyle.backgroundXOffset = null;
  560. backgroundStyle.backgroundYOffset = null;
  561. backgroundStyle.backgroundGraphicZIndex = null;
  562. return this.redrawNode(
  563. id + this.BACKGROUND_ID_SUFFIX,
  564. geometry,
  565. backgroundStyle,
  566. null
  567. );
  568. },
  569. /**
  570. * Method: drawGeometryNode
  571. * Given a node, draw a geometry on the specified layer.
  572. * node and geometry are required arguments, style is optional.
  573. * This method is only called by the render itself.
  574. *
  575. * Parameters:
  576. * node - {DOMElement}
  577. * geometry - {<OpenLayers.Geometry>}
  578. * style - {Object}
  579. *
  580. * Returns:
  581. * {Object} a hash with properties "node" (the drawn node) and "complete"
  582. * (null if parts of the geometry could not be drawn, false if nothing
  583. * could be drawn)
  584. */
  585. drawGeometryNode: function(node, geometry, style) {
  586. style = style || node._style;
  587. var options = {
  588. 'isFilled': style.fill === undefined ?
  589. true :
  590. style.fill,
  591. 'isStroked': style.stroke === undefined ?
  592. !!style.strokeWidth :
  593. style.stroke
  594. };
  595. var drawn;
  596. switch (geometry.CLASS_NAME) {
  597. case "OpenLayers.Geometry.Point":
  598. if(style.graphic === false) {
  599. options.isFilled = false;
  600. options.isStroked = false;
  601. }
  602. drawn = this.drawPoint(node, geometry);
  603. break;
  604. case "OpenLayers.Geometry.LineString":
  605. options.isFilled = false;
  606. drawn = this.drawLineString(node, geometry);
  607. break;
  608. case "OpenLayers.Geometry.LinearRing":
  609. drawn = this.drawLinearRing(node, geometry);
  610. break;
  611. case "OpenLayers.Geometry.Polygon":
  612. drawn = this.drawPolygon(node, geometry);
  613. break;
  614. case "OpenLayers.Geometry.Surface":
  615. drawn = this.drawSurface(node, geometry);
  616. break;
  617. case "OpenLayers.Geometry.Rectangle":
  618. drawn = this.drawRectangle(node, geometry);
  619. break;
  620. default:
  621. break;
  622. }
  623. node._options = options;
  624. //set style
  625. //TBD simplify this
  626. if (drawn != false) {
  627. return {
  628. node: this.setStyle(node, style, options, geometry),
  629. complete: drawn
  630. };
  631. } else {
  632. return false;
  633. }
  634. },
  635. /**
  636. * Method: postDraw
  637. * Things that have do be done after the geometry node is appended
  638. * to its parent node. To be overridden by subclasses.
  639. *
  640. * Parameters:
  641. * node - {DOMElement}
  642. */
  643. postDraw: function(node) {},
  644. /**
  645. * Method: drawPoint
  646. * Virtual function for drawing Point Geometry.
  647. * Should be implemented by subclasses.
  648. * This method is only called by the renderer itself.
  649. *
  650. * Parameters:
  651. * node - {DOMElement}
  652. * geometry - {<OpenLayers.Geometry>}
  653. *
  654. * Returns:
  655. * {DOMElement} or false if the renderer could not draw the point
  656. */
  657. drawPoint: function(node, geometry) {},
  658. /**
  659. * Method: drawLineString
  660. * Virtual function for drawing LineString Geometry.
  661. * Should be implemented by subclasses.
  662. * This method is only called by the renderer itself.
  663. *
  664. * Parameters:
  665. * node - {DOMElement}
  666. * geometry - {<OpenLayers.Geometry>}
  667. *
  668. * Returns:
  669. * {DOMElement} or null if the renderer could not draw all components of
  670. * the linestring, or false if nothing could be drawn
  671. */
  672. drawLineString: function(node, geometry) {},
  673. /**
  674. * Method: drawLinearRing
  675. * Virtual function for drawing LinearRing Geometry.
  676. * Should be implemented by subclasses.
  677. * This method is only called by the renderer itself.
  678. *
  679. * Parameters:
  680. * node - {DOMElement}
  681. * geometry - {<OpenLayers.Geometry>}
  682. *
  683. * Returns:
  684. * {DOMElement} or null if the renderer could not draw all components
  685. * of the linear ring, or false if nothing could be drawn
  686. */
  687. drawLinearRing: function(node, geometry) {},
  688. /**
  689. * Method: drawPolygon
  690. * Virtual function for drawing Polygon Geometry.
  691. * Should be implemented by subclasses.
  692. * This method is only called by the renderer itself.
  693. *
  694. * Parameters:
  695. * node - {DOMElement}
  696. * geometry - {<OpenLayers.Geometry>}
  697. *
  698. * Returns:
  699. * {DOMElement} or null if the renderer could not draw all components
  700. * of the polygon, or false if nothing could be drawn
  701. */
  702. drawPolygon: function(node, geometry) {},
  703. /**
  704. * Method: drawRectangle
  705. * Virtual function for drawing Rectangle Geometry.
  706. * Should be implemented by subclasses.
  707. * This method is only called by the renderer itself.
  708. *
  709. * Parameters:
  710. * node - {DOMElement}
  711. * geometry - {<OpenLayers.Geometry>}
  712. *
  713. * Returns:
  714. * {DOMElement} or false if the renderer could not draw the rectangle
  715. */
  716. drawRectangle: function(node, geometry) {},
  717. /**
  718. * Method: drawCircle
  719. * Virtual function for drawing Circle Geometry.
  720. * Should be implemented by subclasses.
  721. * This method is only called by the renderer itself.
  722. *
  723. * Parameters:
  724. * node - {DOMElement}
  725. * geometry - {<OpenLayers.Geometry>}
  726. *
  727. * Returns:
  728. * {DOMElement} or false if the renderer could not draw the circle
  729. */
  730. drawCircle: function(node, geometry) {},
  731. /**
  732. * Method: drawSurface
  733. * Virtual function for drawing Surface Geometry.
  734. * Should be implemented by subclasses.
  735. * This method is only called by the renderer itself.
  736. *
  737. * Parameters:
  738. * node - {DOMElement}
  739. * geometry - {<OpenLayers.Geometry>}
  740. *
  741. * Returns:
  742. * {DOMElement} or false if the renderer could not draw the surface
  743. */
  744. drawSurface: function(node, geometry) {},
  745. /**
  746. * Method: removeText
  747. * Removes a label
  748. *
  749. * Parameters:
  750. * featureId - {String}
  751. */
  752. removeText: function(featureId) {
  753. var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
  754. if (label) {
  755. this.textRoot.removeChild(label);
  756. }
  757. },
  758. /**
  759. * Method: getFeatureIdFromEvent
  760. *
  761. * Parameters:
  762. * evt - {Object} An <OpenLayers.Event> object
  763. *
  764. * Returns:
  765. * {<OpenLayers.Geometry>} A geometry from an event that
  766. * happened on a layer.
  767. */
  768. getFeatureIdFromEvent: function(evt) {
  769. var target = evt.target;
  770. var useElement = target && target.correspondingUseElement;
  771. var node = useElement ? useElement : (target || evt.srcElement);
  772. var featureId = node._featureId;
  773. return featureId;
  774. },
  775. /**
  776. * Method: eraseGeometry
  777. * Erase a geometry from the renderer. In the case of a multi-geometry,
  778. * we cycle through and recurse on ourselves. Otherwise, we look for a
  779. * node with the geometry.id, destroy its geometry, and remove it from
  780. * the DOM.
  781. *
  782. * Parameters:
  783. * geometry - {<OpenLayers.Geometry>}
  784. * featureId - {String}
  785. */
  786. eraseGeometry: function(geometry, featureId) {
  787. if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
  788. (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
  789. (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
  790. (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
  791. for (var i=0, len=geometry.components.length; i<len; i++) {
  792. this.eraseGeometry(geometry.components[i], featureId);
  793. }
  794. } else {
  795. var element = OpenLayers.Util.getElement(geometry.id);
  796. if (element && element.parentNode) {
  797. if (element.geometry) {
  798. element.geometry.destroy();
  799. element.geometry = null;
  800. }
  801. element.parentNode.removeChild(element);
  802. if (this.indexer) {
  803. this.indexer.remove(element);
  804. }
  805. if (element._style.backgroundGraphic) {
  806. var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
  807. var bElem = OpenLayers.Util.getElement(backgroundId);
  808. if (bElem && bElem.parentNode) {
  809. // No need to destroy the geometry since the element and the background
  810. // node share the same geometry.
  811. bElem.parentNode.removeChild(bElem);
  812. }
  813. }
  814. }
  815. }
  816. },
  817. /**
  818. * Method: nodeFactory
  819. * Create new node of the specified type, with the (optional) specified id.
  820. *
  821. * If node already exists with same ID and a different type, we remove it
  822. * and then call ourselves again to recreate it.
  823. *
  824. * Parameters:
  825. * id - {String}
  826. * type - {String} type Kind of node to draw.
  827. *
  828. * Returns:
  829. * {DOMElement} A new node of the given type and id.
  830. */
  831. nodeFactory: function(id, type) {
  832. var node = OpenLayers.Util.getElement(id);
  833. if (node) {
  834. if (!this.nodeTypeCompare(node, type)) {
  835. node.parentNode.removeChild(node);
  836. node = this.nodeFactory(id, type);
  837. }
  838. } else {
  839. node = this.createNode(type, id);
  840. }
  841. return node;
  842. },
  843. /**
  844. * Method: nodeTypeCompare
  845. *
  846. * Parameters:
  847. * node - {DOMElement}
  848. * type - {String} Kind of node
  849. *
  850. * Returns:
  851. * {Boolean} Whether or not the specified node is of the specified type
  852. * This function must be overridden by subclasses.
  853. */
  854. nodeTypeCompare: function(node, type) {},
  855. /**
  856. * Method: createNode
  857. *
  858. * Parameters:
  859. * type - {String} Kind of node to draw.
  860. * id - {String} Id for node.
  861. *
  862. * Returns:
  863. * {DOMElement} A new node of the given type and id.
  864. * This function must be overridden by subclasses.
  865. */
  866. createNode: function(type, id) {},
  867. /**
  868. * Method: moveRoot
  869. * moves this renderer's root to a different renderer.
  870. *
  871. * Parameters:
  872. * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
  873. */
  874. moveRoot: function(renderer) {
  875. var root = this.root;
  876. if(renderer.root.parentNode == this.rendererRoot) {
  877. root = renderer.root;
  878. }
  879. root.parentNode.removeChild(root);
  880. renderer.rendererRoot.appendChild(root);
  881. },
  882. /**
  883. * Method: getRenderLayerId
  884. * Gets the layer that this renderer's output appears on. If moveRoot was
  885. * used, this will be different from the id of the layer containing the
  886. * features rendered by this renderer.
  887. *
  888. * Returns:
  889. * {String} the id of the output layer.
  890. */
  891. getRenderLayerId: function() {
  892. return this.root.parentNode.parentNode.id;
  893. },
  894. /**
  895. * Method: isComplexSymbol
  896. * Determines if a symbol cannot be rendered using drawCircle
  897. *
  898. * Parameters:
  899. * graphicName - {String}
  900. *
  901. * Returns
  902. * {Boolean} true if the symbol is complex, false if not
  903. */
  904. isComplexSymbol: function(graphicName) {
  905. return (graphicName != "circle") && !!graphicName;
  906. },
  907. CLASS_NAME: "OpenLayers.Renderer.Elements"
  908. });
  909. /**
  910. * Constant: OpenLayers.Renderer.symbol
  911. * Coordinate arrays for well known (named) symbols.
  912. */
  913. OpenLayers.Renderer.symbol = {
  914. "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
  915. 303,215, 231,161, 321,161, 350,75],
  916. "cross": [4,0, 6,0, 6,4, 10,4, 10,6, 6,6, 6,10, 4,10, 4,6, 0,6, 0,4, 4,4,
  917. 4,0],
  918. "x": [0,0, 25,0, 50,35, 75,0, 100,0, 65,50, 100,100, 75,100, 50,65, 25,100, 0,100, 35,50, 0,0],
  919. "square": [0,0, 0,1, 1,1, 1,0, 0,0],
  920. "triangle": [0,10, 10,10, 5,0, 0,10]
  921. };