Point.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  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/Handler.js
  7. * @requires OpenLayers/Geometry/Point.js
  8. */
  9. /**
  10. * Class: OpenLayers.Handler.Point
  11. * Handler to draw a point on the map. Point is displayed on activation,
  12. * moves on mouse move, and is finished on mouse up. The handler triggers
  13. * callbacks for 'done', 'cancel', and 'modify'. The modify callback is
  14. * called with each change in the sketch and will receive the latest point
  15. * drawn. Create a new instance with the <OpenLayers.Handler.Point>
  16. * constructor.
  17. *
  18. * Inherits from:
  19. * - <OpenLayers.Handler>
  20. */
  21. OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {
  22. /**
  23. * Property: point
  24. * {<OpenLayers.Feature.Vector>} The currently drawn point
  25. */
  26. point: null,
  27. /**
  28. * Property: layer
  29. * {<OpenLayers.Layer.Vector>} The temporary drawing layer
  30. */
  31. layer: null,
  32. /**
  33. * APIProperty: multi
  34. * {Boolean} Cast features to multi-part geometries before passing to the
  35. * layer. Default is false.
  36. */
  37. multi: false,
  38. /**
  39. * Property: mouseDown
  40. * {Boolean} The mouse is down
  41. */
  42. mouseDown: false,
  43. /**
  44. * Property: stoppedDown
  45. * {Boolean} Indicate whether the last mousedown stopped the event
  46. * propagation.
  47. */
  48. stoppedDown: null,
  49. /**
  50. * Property: lastDown
  51. * {<OpenLayers.Pixel>} Location of the last mouse down
  52. */
  53. lastDown: null,
  54. /**
  55. * Property: lastUp
  56. * {<OpenLayers.Pixel>}
  57. */
  58. lastUp: null,
  59. /**
  60. * APIProperty: persist
  61. * {Boolean} Leave the feature rendered until destroyFeature is called.
  62. * Default is false. If set to true, the feature remains rendered until
  63. * destroyFeature is called, typically by deactivating the handler or
  64. * starting another drawing.
  65. */
  66. persist: false,
  67. /**
  68. * APIProperty: stopDown
  69. * {Boolean} Stop event propagation on mousedown. Must be false to
  70. * allow "pan while drawing". Defaults to false.
  71. */
  72. stopDown: false,
  73. /**
  74. * APIPropery: stopUp
  75. * {Boolean} Stop event propagation on mouse. Must be false to
  76. * allow "pan while dragging". Defaults to fase.
  77. */
  78. stopUp: false,
  79. /**
  80. * Property: layerOptions
  81. * {Object} Any optional properties to be set on the sketch layer.
  82. */
  83. layerOptions: null,
  84. /**
  85. * APIProperty: pixelTolerance
  86. * {Number} Maximum number of pixels between down and up (mousedown
  87. * and mouseup, or touchstart and touchend) for the handler to
  88. * add a new point. If set to an integer value, if the
  89. * displacement between down and up is great to this value
  90. * no point will be added. Default value is 5.
  91. */
  92. pixelTolerance: 5,
  93. /**
  94. * Property: touch
  95. * {Boolean} Indcates the support of touch events.
  96. */
  97. touch: false,
  98. /**
  99. * Property: lastTouchPx
  100. * {<OpenLayers.Pixel>} The last pixel used to know the distance between
  101. * two touches (for double touch).
  102. */
  103. lastTouchPx: null,
  104. /**
  105. * Constructor: OpenLayers.Handler.Point
  106. * Create a new point handler.
  107. *
  108. * Parameters:
  109. * control - {<OpenLayers.Control>} The control that owns this handler
  110. * callbacks - {Object} An object with a properties whose values are
  111. * functions. Various callbacks described below.
  112. * options - {Object} An optional object with properties to be set on the
  113. * handler
  114. *
  115. * Named callbacks:
  116. * create - Called when a sketch is first created. Callback called with
  117. * the creation point geometry and sketch feature.
  118. * modify - Called with each move of a vertex with the vertex (point)
  119. * geometry and the sketch feature.
  120. * done - Called when the point drawing is finished. The callback will
  121. * recieve a single argument, the point geometry.
  122. * cancel - Called when the handler is deactivated while drawing. The
  123. * cancel callback will receive a geometry.
  124. */
  125. initialize: function(control, callbacks, options) {
  126. if(!(options && options.layerOptions && options.layerOptions.styleMap)) {
  127. this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
  128. }
  129. OpenLayers.Handler.prototype.initialize.apply(this, arguments);
  130. },
  131. /**
  132. * APIMethod: activate
  133. * turn on the handler
  134. */
  135. activate: function() {
  136. if(!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
  137. return false;
  138. }
  139. // create temporary vector layer for rendering geometry sketch
  140. // TBD: this could be moved to initialize/destroy - setting visibility here
  141. var options = OpenLayers.Util.extend({
  142. displayInLayerSwitcher: false,
  143. // indicate that the temp vector layer will never be out of range
  144. // without this, resolution properties must be specified at the
  145. // map-level for this temporary layer to init its resolutions
  146. // correctly
  147. calculateInRange: OpenLayers.Function.True
  148. }, this.layerOptions);
  149. this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
  150. this.map.addLayer(this.layer);
  151. return true;
  152. },
  153. /**
  154. * Method: createFeature
  155. * Add temporary features
  156. *
  157. * Parameters:
  158. * pixel - {<OpenLayers.Pixel>} A pixel location on the map.
  159. */
  160. createFeature: function(pixel) {
  161. var lonlat = this.map.getLonLatFromPixel(pixel);
  162. var geometry = new OpenLayers.Geometry.Point(
  163. lonlat.lon, lonlat.lat
  164. );
  165. this.point = new OpenLayers.Feature.Vector(geometry);
  166. this.callback("create", [this.point.geometry, this.point]);
  167. this.point.geometry.clearBounds();
  168. this.layer.addFeatures([this.point], {silent: true});
  169. },
  170. /**
  171. * APIMethod: deactivate
  172. * turn off the handler
  173. */
  174. deactivate: function() {
  175. if(!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
  176. return false;
  177. }
  178. this.cancel();
  179. // If a layer's map property is set to null, it means that that layer
  180. // isn't added to the map. Since we ourself added the layer to the map
  181. // in activate(), we can assume that if this.layer.map is null it means
  182. // that the layer has been destroyed (as a result of map.destroy() for
  183. // example.
  184. if (this.layer.map != null) {
  185. this.destroyFeature(true);
  186. this.layer.destroy(false);
  187. }
  188. this.layer = null;
  189. this.touch = false;
  190. return true;
  191. },
  192. /**
  193. * Method: destroyFeature
  194. * Destroy the temporary geometries
  195. *
  196. * Parameters:
  197. * force - {Boolean} Destroy even if persist is true.
  198. */
  199. destroyFeature: function(force) {
  200. if(this.layer && (force || !this.persist)) {
  201. this.layer.destroyFeatures();
  202. }
  203. this.point = null;
  204. },
  205. /**
  206. * Method: destroyPersistedFeature
  207. * Destroy the persisted feature.
  208. */
  209. destroyPersistedFeature: function() {
  210. var layer = this.layer;
  211. if(layer && layer.features.length > 1) {
  212. this.layer.features[0].destroy();
  213. }
  214. },
  215. /**
  216. * Method: finalize
  217. * Finish the geometry and call the "done" callback.
  218. *
  219. * Parameters:
  220. * cancel - {Boolean} Call cancel instead of done callback. Default
  221. * is false.
  222. */
  223. finalize: function(cancel) {
  224. var key = cancel ? "cancel" : "done";
  225. this.mouseDown = false;
  226. this.lastDown = null;
  227. this.lastUp = null;
  228. this.lastTouchPx = null;
  229. this.callback(key, [this.geometryClone()]);
  230. this.destroyFeature(cancel);
  231. },
  232. /**
  233. * APIMethod: cancel
  234. * Finish the geometry and call the "cancel" callback.
  235. */
  236. cancel: function() {
  237. this.finalize(true);
  238. },
  239. /**
  240. * Method: click
  241. * Handle clicks. Clicks are stopped from propagating to other listeners
  242. * on map.events or other dom elements.
  243. *
  244. * Parameters:
  245. * evt - {Event} The browser event
  246. *
  247. * Returns:
  248. * {Boolean} Allow event propagation
  249. */
  250. click: function(evt) {
  251. OpenLayers.Event.stop(evt);
  252. return false;
  253. },
  254. /**
  255. * Method: dblclick
  256. * Handle double-clicks. Double-clicks are stopped from propagating to other
  257. * listeners on map.events or other dom elements.
  258. *
  259. * Parameters:
  260. * evt - {Event} The browser event
  261. *
  262. * Returns:
  263. * {Boolean} Allow event propagation
  264. */
  265. dblclick: function(evt) {
  266. OpenLayers.Event.stop(evt);
  267. return false;
  268. },
  269. /**
  270. * Method: modifyFeature
  271. * Modify the existing geometry given a pixel location.
  272. *
  273. * Parameters:
  274. * pixel - {<OpenLayers.Pixel>} A pixel location on the map.
  275. */
  276. modifyFeature: function(pixel) {
  277. if(!this.point) {
  278. this.createFeature(pixel);
  279. }
  280. var lonlat = this.map.getLonLatFromPixel(pixel);
  281. this.point.geometry.x = lonlat.lon;
  282. this.point.geometry.y = lonlat.lat;
  283. this.callback("modify", [this.point.geometry, this.point, false]);
  284. this.point.geometry.clearBounds();
  285. this.drawFeature();
  286. },
  287. /**
  288. * Method: drawFeature
  289. * Render features on the temporary layer.
  290. */
  291. drawFeature: function() {
  292. this.layer.drawFeature(this.point, this.style);
  293. },
  294. /**
  295. * Method: getGeometry
  296. * Return the sketch geometry. If <multi> is true, this will return
  297. * a multi-part geometry.
  298. *
  299. * Returns:
  300. * {<OpenLayers.Geometry.Point>}
  301. */
  302. getGeometry: function() {
  303. var geometry = this.point && this.point.geometry;
  304. if(geometry && this.multi) {
  305. geometry = new OpenLayers.Geometry.MultiPoint([geometry]);
  306. }
  307. return geometry;
  308. },
  309. /**
  310. * Method: geometryClone
  311. * Return a clone of the relevant geometry.
  312. *
  313. * Returns:
  314. * {<OpenLayers.Geometry>}
  315. */
  316. geometryClone: function() {
  317. var geom = this.getGeometry();
  318. return geom && geom.clone();
  319. },
  320. /**
  321. * Method: mousedown
  322. * Handle mousedown.
  323. *
  324. * Parameters:
  325. * evt - {Event} The browser event
  326. *
  327. * Returns:
  328. * {Boolean} Allow event propagation
  329. */
  330. mousedown: function(evt) {
  331. return this.down(evt);
  332. },
  333. /**
  334. * Method: touchstart
  335. * Handle touchstart.
  336. *
  337. * Parameters:
  338. * evt - {Event} The browser event
  339. *
  340. * Returns:
  341. * {Boolean} Allow event propagation
  342. */
  343. touchstart: function(evt) {
  344. if (!this.touch) {
  345. this.touch = true;
  346. // unregister mouse listeners
  347. this.map.events.un({
  348. mousedown: this.mousedown,
  349. mouseup: this.mouseup,
  350. mousemove: this.mousemove,
  351. click: this.click,
  352. dblclick: this.dblclick,
  353. scope: this
  354. });
  355. }
  356. this.lastTouchPx = evt.xy;
  357. return this.down(evt);
  358. },
  359. /**
  360. * Method: mousemove
  361. * Handle mousemove.
  362. *
  363. * Parameters:
  364. * evt - {Event} The browser event
  365. *
  366. * Returns:
  367. * {Boolean} Allow event propagation
  368. */
  369. mousemove: function(evt) {
  370. return this.move(evt);
  371. },
  372. /**
  373. * Method: touchmove
  374. * Handle touchmove.
  375. *
  376. * Parameters:
  377. * evt - {Event} The browser event
  378. *
  379. * Returns:
  380. * {Boolean} Allow event propagation
  381. */
  382. touchmove: function(evt) {
  383. this.lastTouchPx = evt.xy;
  384. return this.move(evt);
  385. },
  386. /**
  387. * Method: mouseup
  388. * Handle mouseup.
  389. *
  390. * Parameters:
  391. * evt - {Event} The browser event
  392. *
  393. * Returns:
  394. * {Boolean} Allow event propagation
  395. */
  396. mouseup: function(evt) {
  397. return this.up(evt);
  398. },
  399. /**
  400. * Method: touchend
  401. * Handle touchend.
  402. *
  403. * Parameters:
  404. * evt - {Event} The browser event
  405. *
  406. * Returns:
  407. * {Boolean} Allow event propagation
  408. */
  409. touchend: function(evt) {
  410. evt.xy = this.lastTouchPx;
  411. return this.up(evt);
  412. },
  413. /**
  414. * Method: down
  415. * Handle mousedown and touchstart. Adjust the geometry and redraw.
  416. * Return determines whether to propagate the event on the map.
  417. *
  418. * Parameters:
  419. * evt - {Event} The browser event
  420. *
  421. * Returns:
  422. * {Boolean} Allow event propagation
  423. */
  424. down: function(evt) {
  425. this.mouseDown = true;
  426. this.lastDown = evt.xy;
  427. if(!this.touch) { // no point displayed until up on touch devices
  428. this.modifyFeature(evt.xy);
  429. }
  430. this.stoppedDown = this.stopDown;
  431. return !this.stopDown;
  432. },
  433. /**
  434. * Method: move
  435. * Handle mousemove and touchmove. Adjust the geometry and redraw.
  436. * Return determines whether to propagate the event on the map.
  437. *
  438. * Parameters:
  439. * evt - {Event} The browser event
  440. *
  441. * Returns:
  442. * {Boolean} Allow event propagation
  443. */
  444. move: function (evt) {
  445. if(!this.touch // no point displayed until up on touch devices
  446. && (!this.mouseDown || this.stoppedDown)) {
  447. this.modifyFeature(evt.xy);
  448. }
  449. return true;
  450. },
  451. /**
  452. * Method: up
  453. * Handle mouseup and touchend. Send the latest point in the geometry to the control.
  454. * Return determines whether to propagate the event on the map.
  455. *
  456. * Parameters:
  457. * evt - {Event} The browser event
  458. *
  459. * Returns:
  460. * {Boolean} Allow event propagation
  461. */
  462. up: function (evt) {
  463. this.mouseDown = false;
  464. this.stoppedDown = this.stopDown;
  465. // check keyboard modifiers
  466. if(!this.checkModifiers(evt)) {
  467. return true;
  468. }
  469. // ignore double-clicks
  470. if (this.lastUp && this.lastUp.equals(evt.xy)) {
  471. return true;
  472. }
  473. if (this.lastDown && this.passesTolerance(this.lastDown, evt.xy,
  474. this.pixelTolerance)) {
  475. if (this.touch) {
  476. this.modifyFeature(evt.xy);
  477. }
  478. if(this.persist) {
  479. this.destroyPersistedFeature();
  480. }
  481. this.lastUp = evt.xy;
  482. this.finalize();
  483. return !this.stopUp;
  484. } else {
  485. return true;
  486. }
  487. },
  488. /**
  489. * Method: mouseout
  490. * Handle mouse out. For better user experience reset mouseDown
  491. * and stoppedDown when the mouse leaves the map viewport.
  492. *
  493. * Parameters:
  494. * evt - {Event} The browser event
  495. */
  496. mouseout: function(evt) {
  497. if(OpenLayers.Util.mouseLeft(evt, this.map.eventsDiv)) {
  498. this.stoppedDown = this.stopDown;
  499. this.mouseDown = false;
  500. }
  501. },
  502. /**
  503. * Method: passesTolerance
  504. * Determine whether the event is within the optional pixel tolerance.
  505. *
  506. * Returns:
  507. * {Boolean} The event is within the pixel tolerance (if specified).
  508. */
  509. passesTolerance: function(pixel1, pixel2, tolerance) {
  510. var passes = true;
  511. if (tolerance != null && pixel1 && pixel2) {
  512. var dist = pixel1.distanceTo(pixel2);
  513. if (dist > tolerance) {
  514. passes = false;
  515. }
  516. }
  517. return passes;
  518. },
  519. CLASS_NAME: "OpenLayers.Handler.Point"
  520. });