Canvas.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  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.Renderer.Canvas
  10. * A renderer based on the 2D 'canvas' drawing element.
  11. *
  12. * Inherits:
  13. * - <OpenLayers.Renderer>
  14. */
  15. OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
  16. /**
  17. * APIProperty: hitDetection
  18. * {Boolean} Allow for hit detection of features. Default is true.
  19. */
  20. hitDetection: true,
  21. /**
  22. * Property: hitOverflow
  23. * {Number} The method for converting feature identifiers to color values
  24. * supports 16777215 sequential values. Two features cannot be
  25. * predictably detected if their identifiers differ by more than this
  26. * value. The hitOverflow allows for bigger numbers (but the
  27. * difference in values is still limited).
  28. */
  29. hitOverflow: 0,
  30. /**
  31. * Property: canvas
  32. * {Canvas} The canvas context object.
  33. */
  34. canvas: null,
  35. /**
  36. * Property: features
  37. * {Object} Internal object of feature/style pairs for use in redrawing the layer.
  38. */
  39. features: null,
  40. /**
  41. * Property: pendingRedraw
  42. * {Boolean} The renderer needs a redraw call to render features added while
  43. * the renderer was locked.
  44. */
  45. pendingRedraw: false,
  46. /**
  47. * Constructor: OpenLayers.Renderer.Canvas
  48. *
  49. * Parameters:
  50. * containerID - {<String>}
  51. * options - {Object} Optional properties to be set on the renderer.
  52. */
  53. initialize: function(containerID, options) {
  54. OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
  55. this.root = document.createElement("canvas");
  56. this.container.appendChild(this.root);
  57. this.canvas = this.root.getContext("2d");
  58. this.features = {};
  59. if (this.hitDetection) {
  60. this.hitCanvas = document.createElement("canvas");
  61. this.hitContext = this.hitCanvas.getContext("2d");
  62. }
  63. },
  64. /**
  65. * Method: eraseGeometry
  66. * Erase a geometry from the renderer. Because the Canvas renderer has
  67. * 'memory' of the features that it has drawn, we have to remove the
  68. * feature so it doesn't redraw.
  69. *
  70. * Parameters:
  71. * geometry - {<OpenLayers.Geometry>}
  72. * featureId - {String}
  73. */
  74. eraseGeometry: function(geometry, featureId) {
  75. this.eraseFeatures(this.features[featureId][0]);
  76. },
  77. /**
  78. * APIMethod: supported
  79. *
  80. * Returns:
  81. * {Boolean} Whether or not the browser supports the renderer class
  82. */
  83. supported: function() {
  84. var canvas = document.createElement("canvas");
  85. return !!canvas.getContext;
  86. },
  87. /**
  88. * Method: setSize
  89. * Sets the size of the drawing surface.
  90. *
  91. * Once the size is updated, redraw the canvas.
  92. *
  93. * Parameters:
  94. * size - {<OpenLayers.Size>}
  95. */
  96. setSize: function(size) {
  97. this.size = size.clone();
  98. var root = this.root;
  99. root.style.width = size.w + "px";
  100. root.style.height = size.h + "px";
  101. root.width = size.w;
  102. root.height = size.h;
  103. this.resolution = null;
  104. if (this.hitDetection) {
  105. var hitCanvas = this.hitCanvas;
  106. hitCanvas.style.width = size.w + "px";
  107. hitCanvas.style.height = size.h + "px";
  108. hitCanvas.width = size.w;
  109. hitCanvas.height = size.h;
  110. }
  111. },
  112. /**
  113. * Method: drawFeature
  114. * Draw the feature. Stores the feature in the features list,
  115. * then redraws the layer.
  116. *
  117. * Parameters:
  118. * feature - {<OpenLayers.Feature.Vector>}
  119. * style - {<Object>}
  120. *
  121. * Returns:
  122. * {Boolean} The feature has been drawn completely. If the feature has no
  123. * geometry, undefined will be returned. If the feature is not rendered
  124. * for other reasons, false will be returned.
  125. */
  126. drawFeature: function(feature, style) {
  127. var rendered;
  128. if (feature.geometry) {
  129. style = this.applyDefaultSymbolizer(style || feature.style);
  130. // don't render if display none or feature outside extent
  131. var bounds = feature.geometry.getBounds();
  132. rendered = (style.display !== "none") && !!bounds &&
  133. bounds.intersectsBounds(this.extent);
  134. if (rendered) {
  135. // keep track of what we have rendered for redraw
  136. this.features[feature.id] = [feature, style];
  137. }
  138. else {
  139. // remove from features tracked for redraw
  140. delete(this.features[feature.id]);
  141. }
  142. this.pendingRedraw = true;
  143. }
  144. if (this.pendingRedraw && !this.locked) {
  145. this.redraw();
  146. this.pendingRedraw = false;
  147. }
  148. return rendered;
  149. },
  150. /**
  151. * Method: drawGeometry
  152. * Used when looping (in redraw) over the features; draws
  153. * the canvas.
  154. *
  155. * Parameters:
  156. * geometry - {<OpenLayers.Geometry>}
  157. * style - {Object}
  158. */
  159. drawGeometry: function(geometry, style, featureId) {
  160. var className = geometry.CLASS_NAME;
  161. if ((className == "OpenLayers.Geometry.Collection") ||
  162. (className == "OpenLayers.Geometry.MultiPoint") ||
  163. (className == "OpenLayers.Geometry.MultiLineString") ||
  164. (className == "OpenLayers.Geometry.MultiPolygon")) {
  165. for (var i = 0; i < geometry.components.length; i++) {
  166. this.drawGeometry(geometry.components[i], style, featureId);
  167. }
  168. return;
  169. }
  170. switch (geometry.CLASS_NAME) {
  171. case "OpenLayers.Geometry.Point":
  172. this.drawPoint(geometry, style, featureId);
  173. break;
  174. case "OpenLayers.Geometry.LineString":
  175. this.drawLineString(geometry, style, featureId);
  176. break;
  177. case "OpenLayers.Geometry.LinearRing":
  178. this.drawLinearRing(geometry, style, featureId);
  179. break;
  180. case "OpenLayers.Geometry.Polygon":
  181. this.drawPolygon(geometry, style, featureId);
  182. break;
  183. default:
  184. break;
  185. }
  186. },
  187. /**
  188. * Method: drawExternalGraphic
  189. * Called to draw External graphics.
  190. *
  191. * Parameters:
  192. * geometry - {<OpenLayers.Geometry>}
  193. * style - {Object}
  194. * featureId - {String}
  195. */
  196. drawExternalGraphic: function(geometry, style, featureId) {
  197. var img = new Image();
  198. if (style.graphicTitle) {
  199. img.title = style.graphicTitle;
  200. }
  201. var width = style.graphicWidth || style.graphicHeight;
  202. var height = style.graphicHeight || style.graphicWidth;
  203. width = width ? width : style.pointRadius * 2;
  204. height = height ? height : style.pointRadius * 2;
  205. var xOffset = (style.graphicXOffset != undefined) ?
  206. style.graphicXOffset : -(0.5 * width);
  207. var yOffset = (style.graphicYOffset != undefined) ?
  208. style.graphicYOffset : -(0.5 * height);
  209. var opacity = style.graphicOpacity || style.fillOpacity;
  210. var onLoad = function() {
  211. if(!this.features[featureId]) {
  212. return;
  213. }
  214. var pt = this.getLocalXY(geometry);
  215. var p0 = pt[0];
  216. var p1 = pt[1];
  217. if(!isNaN(p0) && !isNaN(p1)) {
  218. var x = (p0 + xOffset) | 0;
  219. var y = (p1 + yOffset) | 0;
  220. var canvas = this.canvas;
  221. canvas.globalAlpha = opacity;
  222. var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||
  223. (OpenLayers.Renderer.Canvas.drawImageScaleFactor =
  224. /android 2.1/.test(navigator.userAgent.toLowerCase()) ?
  225. // 320 is the screen width of the G1 phone, for
  226. // which drawImage works out of the box.
  227. 320 / window.screen.width : 1
  228. );
  229. canvas.drawImage(
  230. img, x*factor, y*factor, width*factor, height*factor
  231. );
  232. if (this.hitDetection) {
  233. this.setHitContextStyle("fill", featureId);
  234. this.hitContext.fillRect(x, y, width, height);
  235. }
  236. }
  237. };
  238. img.onload = OpenLayers.Function.bind(onLoad, this);
  239. img.src = style.externalGraphic;
  240. },
  241. /**
  242. * Method: setCanvasStyle
  243. * Prepare the canvas for drawing by setting various global settings.
  244. *
  245. * Parameters:
  246. * type - {String} one of 'stroke', 'fill', or 'reset'
  247. * style - {Object} Symbolizer hash
  248. */
  249. setCanvasStyle: function(type, style) {
  250. if (type === "fill") {
  251. this.canvas.globalAlpha = style['fillOpacity'];
  252. this.canvas.fillStyle = style['fillColor'];
  253. } else if (type === "stroke") {
  254. this.canvas.globalAlpha = style['strokeOpacity'];
  255. this.canvas.strokeStyle = style['strokeColor'];
  256. this.canvas.lineWidth = style['strokeWidth'];
  257. } else {
  258. this.canvas.globalAlpha = 0;
  259. this.canvas.lineWidth = 1;
  260. }
  261. },
  262. /**
  263. * Method: featureIdToHex
  264. * Convert a feature ID string into an RGB hex string.
  265. *
  266. * Parameters:
  267. * featureId - {String} Feature id
  268. *
  269. * Returns:
  270. * {String} RGB hex string.
  271. */
  272. featureIdToHex: function(featureId) {
  273. var id = Number(featureId.split("_").pop()) + 1; // zero for no feature
  274. if (id >= 16777216) {
  275. this.hitOverflow = id - 16777215;
  276. id = id % 16777216 + 1;
  277. }
  278. var hex = "000000" + id.toString(16);
  279. var len = hex.length;
  280. hex = "#" + hex.substring(len-6, len);
  281. return hex;
  282. },
  283. /**
  284. * Method: setHitContextStyle
  285. * Prepare the hit canvas for drawing by setting various global settings.
  286. *
  287. * Parameters:
  288. * type - {String} one of 'stroke', 'fill', or 'reset'
  289. * featureId - {String} The feature id.
  290. * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.
  291. */
  292. setHitContextStyle: function(type, featureId, symbolizer) {
  293. var hex = this.featureIdToHex(featureId);
  294. if (type == "fill") {
  295. this.hitContext.globalAlpha = 1.0;
  296. this.hitContext.fillStyle = hex;
  297. } else if (type == "stroke") {
  298. this.hitContext.globalAlpha = 1.0;
  299. this.hitContext.strokeStyle = hex;
  300. // bump up stroke width to deal with antialiasing
  301. this.hitContext.lineWidth = symbolizer.strokeWidth + 2;
  302. } else {
  303. this.hitContext.globalAlpha = 0;
  304. this.hitContext.lineWidth = 1;
  305. }
  306. },
  307. /**
  308. * Method: drawPoint
  309. * This method is only called by the renderer itself.
  310. *
  311. * Parameters:
  312. * geometry - {<OpenLayers.Geometry>}
  313. * style - {Object}
  314. * featureId - {String}
  315. */
  316. drawPoint: function(geometry, style, featureId) {
  317. if(style.graphic !== false) {
  318. if(style.externalGraphic) {
  319. this.drawExternalGraphic(geometry, style, featureId);
  320. } else {
  321. var pt = this.getLocalXY(geometry);
  322. var p0 = pt[0];
  323. var p1 = pt[1];
  324. if(!isNaN(p0) && !isNaN(p1)) {
  325. var twoPi = Math.PI*2;
  326. var radius = style.pointRadius;
  327. if(style.fill !== false) {
  328. this.setCanvasStyle("fill", style);
  329. this.canvas.beginPath();
  330. this.canvas.arc(p0, p1, radius, 0, twoPi, true);
  331. this.canvas.fill();
  332. if (this.hitDetection) {
  333. this.setHitContextStyle("fill", featureId, style);
  334. this.hitContext.beginPath();
  335. this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
  336. this.hitContext.fill();
  337. }
  338. }
  339. if(style.stroke !== false) {
  340. this.setCanvasStyle("stroke", style);
  341. this.canvas.beginPath();
  342. this.canvas.arc(p0, p1, radius, 0, twoPi, true);
  343. this.canvas.stroke();
  344. if (this.hitDetection) {
  345. this.setHitContextStyle("stroke", featureId, style);
  346. this.hitContext.beginPath();
  347. this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
  348. this.hitContext.stroke();
  349. }
  350. this.setCanvasStyle("reset");
  351. }
  352. }
  353. }
  354. }
  355. },
  356. /**
  357. * Method: drawLineString
  358. * This method is only called by the renderer itself.
  359. *
  360. * Parameters:
  361. * geometry - {<OpenLayers.Geometry>}
  362. * style - {Object}
  363. * featureId - {String}
  364. */
  365. drawLineString: function(geometry, style, featureId) {
  366. style = OpenLayers.Util.applyDefaults({fill: false}, style);
  367. this.drawLinearRing(geometry, style, featureId);
  368. },
  369. /**
  370. * Method: drawLinearRing
  371. * This method is only called by the renderer itself.
  372. *
  373. * Parameters:
  374. * geometry - {<OpenLayers.Geometry>}
  375. * style - {Object}
  376. * featureId - {String}
  377. */
  378. drawLinearRing: function(geometry, style, featureId) {
  379. if (style.fill !== false) {
  380. this.setCanvasStyle("fill", style);
  381. this.renderPath(this.canvas, geometry, style, featureId, "fill");
  382. if (this.hitDetection) {
  383. this.setHitContextStyle("fill", featureId, style);
  384. this.renderPath(this.hitContext, geometry, style, featureId, "fill");
  385. }
  386. }
  387. if (style.stroke !== false) {
  388. this.setCanvasStyle("stroke", style);
  389. this.renderPath(this.canvas, geometry, style, featureId, "stroke");
  390. if (this.hitDetection) {
  391. this.setHitContextStyle("stroke", featureId, style);
  392. this.renderPath(this.hitContext, geometry, style, featureId, "stroke");
  393. }
  394. }
  395. this.setCanvasStyle("reset");
  396. },
  397. /**
  398. * Method: renderPath
  399. * Render a path with stroke and optional fill.
  400. */
  401. renderPath: function(context, geometry, style, featureId, type) {
  402. var components = geometry.components;
  403. var len = components.length;
  404. context.beginPath();
  405. var start = this.getLocalXY(components[0]);
  406. var x = start[0];
  407. var y = start[1];
  408. if (!isNaN(x) && !isNaN(y)) {
  409. context.moveTo(start[0], start[1]);
  410. for (var i=1; i<len; ++i) {
  411. var pt = this.getLocalXY(components[i]);
  412. context.lineTo(pt[0], pt[1]);
  413. }
  414. if (type === "fill") {
  415. context.fill();
  416. } else {
  417. context.stroke();
  418. }
  419. }
  420. },
  421. /**
  422. * Method: drawPolygon
  423. * This method is only called by the renderer itself.
  424. *
  425. * Parameters:
  426. * geometry - {<OpenLayers.Geometry>}
  427. * style - {Object}
  428. * featureId - {String}
  429. */
  430. drawPolygon: function(geometry, style, featureId) {
  431. var components = geometry.components;
  432. var len = components.length;
  433. this.drawLinearRing(components[0], style, featureId);
  434. // erase inner rings
  435. for (var i=1; i<len; ++i) {
  436. /**
  437. * Note that this is overly agressive. Here we punch holes through
  438. * all previously rendered features on the same canvas. A better
  439. * solution for polygons with interior rings would be to draw the
  440. * polygon on a sketch canvas first. We could erase all holes
  441. * there and then copy the drawing to the layer canvas.
  442. * TODO: http://trac.osgeo.org/openlayers/ticket/3130
  443. */
  444. this.canvas.globalCompositeOperation = "destination-out";
  445. if (this.hitDetection) {
  446. this.hitContext.globalCompositeOperation = "destination-out";
  447. }
  448. this.drawLinearRing(
  449. components[i],
  450. OpenLayers.Util.applyDefaults({stroke: false, fillOpacity: 1.0}, style),
  451. featureId
  452. );
  453. this.canvas.globalCompositeOperation = "source-over";
  454. if (this.hitDetection) {
  455. this.hitContext.globalCompositeOperation = "source-over";
  456. }
  457. this.drawLinearRing(
  458. components[i],
  459. OpenLayers.Util.applyDefaults({fill: false}, style),
  460. featureId
  461. );
  462. }
  463. },
  464. /**
  465. * Method: drawText
  466. * This method is only called by the renderer itself.
  467. *
  468. * Parameters:
  469. * location - {<OpenLayers.Point>}
  470. * style - {Object}
  471. */
  472. drawText: function(location, style) {
  473. style = OpenLayers.Util.extend({
  474. fontColor: "#000000",
  475. labelAlign: "cm"
  476. }, style);
  477. var pt = this.getLocalXY(location);
  478. this.setCanvasStyle("reset");
  479. this.canvas.fillStyle = style.fontColor;
  480. this.canvas.globalAlpha = style.fontOpacity || 1.0;
  481. var fontStyle = [style.fontStyle ? style.fontStyle : "normal",
  482. "normal", // "font-variant" not supported
  483. style.fontWeight ? style.fontWeight : "normal",
  484. style.fontSize ? style.fontSize : "1em",
  485. style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
  486. var labelRows = style.label.split('\n');
  487. var numRows = labelRows.length;
  488. if (this.canvas.fillText) {
  489. // HTML5
  490. this.canvas.font = fontStyle;
  491. this.canvas.textAlign =
  492. OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
  493. "center";
  494. this.canvas.textBaseline =
  495. OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||
  496. "middle";
  497. var vfactor =
  498. OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
  499. if (vfactor == null) {
  500. vfactor = -.5;
  501. }
  502. var lineHeight =
  503. this.canvas.measureText('Mg').height ||
  504. this.canvas.measureText('xx').width;
  505. pt[1] += lineHeight*vfactor*(numRows-1);
  506. for (var i = 0; i < numRows; i++) {
  507. this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i));
  508. }
  509. } else if (this.canvas.mozDrawText) {
  510. // Mozilla pre-Gecko1.9.1 (<FF3.1)
  511. this.canvas.mozTextStyle = fontStyle;
  512. // No built-in text alignment, so we measure and adjust the position
  513. var hfactor =
  514. OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];
  515. if (hfactor == null) {
  516. hfactor = -.5;
  517. }
  518. var vfactor =
  519. OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
  520. if (vfactor == null) {
  521. vfactor = -.5;
  522. }
  523. var lineHeight = this.canvas.mozMeasureText('xx');
  524. pt[1] += lineHeight*(1 + (vfactor*numRows));
  525. for (var i = 0; i < numRows; i++) {
  526. var x = pt[0] + (hfactor*this.canvas.mozMeasureText(labelRows[i]));
  527. var y = pt[1] + (i*lineHeight);
  528. this.canvas.translate(x, y);
  529. this.canvas.mozDrawText(labelRows[i]);
  530. this.canvas.translate(-x, -y);
  531. }
  532. }
  533. this.setCanvasStyle("reset");
  534. },
  535. /**
  536. * Method: getLocalXY
  537. * transform geographic xy into pixel xy
  538. *
  539. * Parameters:
  540. * point - {<OpenLayers.Geometry.Point>}
  541. */
  542. getLocalXY: function(point) {
  543. var resolution = this.getResolution();
  544. var extent = this.extent;
  545. var x = (point.x / resolution + (-extent.left / resolution));
  546. var y = ((extent.top / resolution) - point.y / resolution);
  547. return [x, y];
  548. },
  549. /**
  550. * Method: clear
  551. * Clear all vectors from the renderer.
  552. */
  553. clear: function() {
  554. var height = this.root.height;
  555. var width = this.root.width;
  556. this.canvas.clearRect(0, 0, width, height);
  557. this.features = {};
  558. if (this.hitDetection) {
  559. this.hitContext.clearRect(0, 0, width, height);
  560. }
  561. },
  562. /**
  563. * Method: getFeatureIdFromEvent
  564. * Returns a feature id from an event on the renderer.
  565. *
  566. * Parameters:
  567. * evt - {<OpenLayers.Event>}
  568. *
  569. * Returns:
  570. * {<OpenLayers.Feature.Vector} A feature or null. This method returns a
  571. * feature instead of a feature id to avoid an unnecessary lookup on the
  572. * layer.
  573. */
  574. getFeatureIdFromEvent: function(evt) {
  575. var feature = null;
  576. if (this.hitDetection) {
  577. // this dragging check should go in the feature handler
  578. if (!this.map.dragging) {
  579. var xy = evt.xy;
  580. var x = xy.x | 0;
  581. var y = xy.y | 0;
  582. var data = this.hitContext.getImageData(x, y, 1, 1).data;
  583. if (data[3] === 255) { // antialiased
  584. var id = data[2] + (256 * (data[1] + (256 * data[0])));
  585. if (id) {
  586. feature = this.features["OpenLayers.Feature.Vector_" + (id - 1 + this.hitOverflow)][0];
  587. }
  588. }
  589. }
  590. }
  591. return feature;
  592. },
  593. /**
  594. * Method: eraseFeatures
  595. * This is called by the layer to erase features; removes the feature from
  596. * the list, then redraws the layer.
  597. *
  598. * Parameters:
  599. * features - {Array(<OpenLayers.Feature.Vector>)}
  600. */
  601. eraseFeatures: function(features) {
  602. if(!(OpenLayers.Util.isArray(features))) {
  603. features = [features];
  604. }
  605. for(var i=0; i<features.length; ++i) {
  606. delete this.features[features[i].id];
  607. }
  608. this.redraw();
  609. },
  610. /**
  611. * Method: redraw
  612. * The real 'meat' of the function: any time things have changed,
  613. * redraw() can be called to loop over all the data and (you guessed
  614. * it) redraw it. Unlike Elements-based Renderers, we can't interact
  615. * with things once they're drawn, to remove them, for example, so
  616. * instead we have to just clear everything and draw from scratch.
  617. */
  618. redraw: function() {
  619. if (!this.locked) {
  620. var height = this.root.height;
  621. var width = this.root.width;
  622. this.canvas.clearRect(0, 0, width, height);
  623. if (this.hitDetection) {
  624. this.hitContext.clearRect(0, 0, width, height);
  625. }
  626. var labelMap = [];
  627. var feature, style;
  628. for (var id in this.features) {
  629. if (!this.features.hasOwnProperty(id)) { continue; }
  630. feature = this.features[id][0];
  631. style = this.features[id][1];
  632. this.drawGeometry(feature.geometry, style, feature.id);
  633. if(style.label) {
  634. labelMap.push([feature, style]);
  635. }
  636. }
  637. var item;
  638. for (var i=0, len=labelMap.length; i<len; ++i) {
  639. item = labelMap[i];
  640. this.drawText(item[0].geometry.getCentroid(), item[1]);
  641. }
  642. }
  643. },
  644. CLASS_NAME: "OpenLayers.Renderer.Canvas"
  645. });
  646. /**
  647. * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
  648. * {Object}
  649. */
  650. OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
  651. "l": "left",
  652. "r": "right",
  653. "t": "top",
  654. "b": "bottom"
  655. };
  656. /**
  657. * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR
  658. * {Object}
  659. */
  660. OpenLayers.Renderer.Canvas.LABEL_FACTOR = {
  661. "l": 0,
  662. "r": -1,
  663. "t": 0,
  664. "b": -1
  665. };
  666. /**
  667. * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor
  668. * {Number} Scale factor to apply to the canvas drawImage arguments. This
  669. * is always 1 except for Android 2.1 devices, to work around
  670. * http://code.google.com/p/android/issues/detail?id=5141.
  671. */
  672. OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;