Point.js 15 KB

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