Polygon.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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/Path.js
  7. * @requires OpenLayers/Geometry/Polygon.js
  8. */
  9. /**
  10. * Class: OpenLayers.Handler.Polygon
  11. * Handler to draw a polygon on the map. Polygon is displayed on mouse down,
  12. * moves on mouse move, and is finished on mouse up.
  13. *
  14. * Inherits from:
  15. * - <OpenLayers.Handler.Path>
  16. * - <OpenLayers.Handler>
  17. */
  18. OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
  19. /**
  20. * APIProperty: holeModifier
  21. * {String} Key modifier to trigger hole digitizing. Acceptable values are
  22. * "altKey", "shiftKey", or "ctrlKey". If not set, no hole digitizing
  23. * will take place. Default is null.
  24. */
  25. holeModifier: null,
  26. /**
  27. * Property: drawingHole
  28. * {Boolean} Currently drawing an interior ring.
  29. */
  30. drawingHole: false,
  31. /**
  32. * Parameter: polygon
  33. * {<OpenLayers.Feature.Vector>}
  34. */
  35. polygon: null,
  36. /**
  37. * Constructor: OpenLayers.Handler.Polygon
  38. * Create a Polygon Handler.
  39. *
  40. * Parameters:
  41. * control - {<OpenLayers.Control>} The control that owns this handler
  42. * callbacks - {Object} An object with a properties whose values are
  43. * functions. Various callbacks described below.
  44. * options - {Object} An optional object with properties to be set on the
  45. * handler
  46. *
  47. * Named callbacks:
  48. * create - Called when a sketch is first created. Callback called with
  49. * the creation point geometry and sketch feature.
  50. * modify - Called with each move of a vertex with the vertex (point)
  51. * geometry and the sketch feature.
  52. * point - Called as each point is added. Receives the new point geometry.
  53. * done - Called when the point drawing is finished. The callback will
  54. * recieve a single argument, the polygon geometry.
  55. * cancel - Called when the handler is deactivated while drawing. The
  56. * cancel callback will receive a geometry.
  57. */
  58. initialize: function(control, callbacks, options) {
  59. OpenLayers.Handler.Path.prototype.initialize.apply(this, arguments);
  60. },
  61. /**
  62. * Method: createFeature
  63. * Add temporary geometries
  64. *
  65. * Parameters:
  66. * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
  67. * feature.
  68. */
  69. createFeature: function(pixel) {
  70. var lonlat = this.map.getLonLatFromPixel(pixel);
  71. var geometry = new OpenLayers.Geometry.Point(
  72. lonlat.lon, lonlat.lat
  73. );
  74. this.point = new OpenLayers.Feature.Vector(geometry);
  75. this.line = new OpenLayers.Feature.Vector(
  76. new OpenLayers.Geometry.LinearRing([this.point.geometry])
  77. );
  78. this.polygon = new OpenLayers.Feature.Vector(
  79. new OpenLayers.Geometry.Polygon([this.line.geometry])
  80. );
  81. this.callback("create", [this.point.geometry, this.getSketch()]);
  82. this.point.geometry.clearBounds();
  83. this.layer.addFeatures([this.polygon, this.point], {silent: true});
  84. },
  85. /**
  86. * Method: addPoint
  87. * Add point to geometry.
  88. *
  89. * Parameters:
  90. * pixel - {<OpenLayers.Pixel>} The pixel location for the new point.
  91. */
  92. addPoint: function(pixel) {
  93. if(!this.drawingHole && this.holeModifier &&
  94. this.evt && this.evt[this.holeModifier]) {
  95. var geometry = this.point.geometry;
  96. var features = this.control.layer.features;
  97. var candidate, polygon;
  98. // look for intersections, last drawn gets priority
  99. for (var i=features.length-1; i>=0; --i) {
  100. candidate = features[i].geometry;
  101. if ((candidate instanceof OpenLayers.Geometry.Polygon ||
  102. candidate instanceof OpenLayers.Geometry.MultiPolygon) &&
  103. candidate.intersects(geometry)) {
  104. polygon = features[i];
  105. this.control.layer.removeFeatures([polygon], {silent: true});
  106. this.control.layer.events.registerPriority(
  107. "sketchcomplete", this, this.finalizeInteriorRing
  108. );
  109. this.control.layer.events.registerPriority(
  110. "sketchmodified", this, this.enforceTopology
  111. );
  112. polygon.geometry.addComponent(this.line.geometry);
  113. this.polygon = polygon;
  114. this.drawingHole = true;
  115. break;
  116. }
  117. }
  118. }
  119. OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments);
  120. },
  121. /**
  122. * Method: getCurrentPointIndex
  123. *
  124. * Returns:
  125. * {Number} The index of the most recently drawn point.
  126. */
  127. getCurrentPointIndex: function() {
  128. return this.line.geometry.components.length - 2;
  129. },
  130. /**
  131. * Method: enforceTopology
  132. * Simple topology enforcement for drawing interior rings. Ensures vertices
  133. * of interior rings are contained by exterior ring. Other topology
  134. * rules are enforced in <finalizeInteriorRing> to allow drawing of
  135. * rings that intersect only during the sketch (e.g. a "C" shaped ring
  136. * that nearly encloses another ring).
  137. */
  138. enforceTopology: function(event) {
  139. var point = event.vertex;
  140. var components = this.line.geometry.components;
  141. // ensure that vertices of interior ring are contained by exterior ring
  142. if (!this.polygon.geometry.intersects(point)) {
  143. var last = components[components.length-3];
  144. point.x = last.x;
  145. point.y = last.y;
  146. }
  147. },
  148. /**
  149. * Method: finishGeometry
  150. * Finish the geometry and send it back to the control.
  151. */
  152. finishGeometry: function() {
  153. var index = this.line.geometry.components.length - 2;
  154. this.line.geometry.removeComponent(this.line.geometry.components[index]);
  155. this.removePoint();
  156. this.finalize();
  157. },
  158. /**
  159. * Method: finalizeInteriorRing
  160. * Enforces that new ring has some area and doesn't contain vertices of any
  161. * other rings.
  162. */
  163. finalizeInteriorRing: function() {
  164. var ring = this.line.geometry;
  165. // ensure that ring has some area
  166. var modified = (ring.getArea() !== 0);
  167. if (modified) {
  168. // ensure that new ring doesn't intersect any other rings
  169. var rings = this.polygon.geometry.components;
  170. for (var i=rings.length-2; i>=0; --i) {
  171. if (ring.intersects(rings[i])) {
  172. modified = false;
  173. break;
  174. }
  175. }
  176. if (modified) {
  177. // ensure that new ring doesn't contain any other rings
  178. var target;
  179. outer: for (var i=rings.length-2; i>0; --i) {
  180. var points = rings[i].components;
  181. for (var j=0, jj=points.length; j<jj; ++j) {
  182. if (ring.containsPoint(points[j])) {
  183. modified = false;
  184. break outer;
  185. }
  186. }
  187. }
  188. }
  189. }
  190. if (modified) {
  191. if (this.polygon.state !== OpenLayers.State.INSERT) {
  192. this.polygon.state = OpenLayers.State.UPDATE;
  193. }
  194. } else {
  195. this.polygon.geometry.removeComponent(ring);
  196. }
  197. this.restoreFeature();
  198. return false;
  199. },
  200. /**
  201. * APIMethod: cancel
  202. * Finish the geometry and call the "cancel" callback.
  203. */
  204. cancel: function() {
  205. if (this.drawingHole) {
  206. this.polygon.geometry.removeComponent(this.line.geometry);
  207. this.restoreFeature(true);
  208. }
  209. return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments);
  210. },
  211. /**
  212. * Method: restoreFeature
  213. * Move the feature from the sketch layer to the target layer.
  214. *
  215. * Properties:
  216. * cancel - {Boolean} Cancel drawing. If falsey, the "sketchcomplete" event
  217. * will be fired.
  218. */
  219. restoreFeature: function(cancel) {
  220. this.control.layer.events.unregister(
  221. "sketchcomplete", this, this.finalizeInteriorRing
  222. );
  223. this.control.layer.events.unregister(
  224. "sketchmodified", this, this.enforceTopology
  225. );
  226. this.layer.removeFeatures([this.polygon], {silent: true});
  227. this.control.layer.addFeatures([this.polygon], {silent: true});
  228. this.drawingHole = false;
  229. if (!cancel) {
  230. // Re-trigger "sketchcomplete" so other listeners can do their
  231. // business. While this is somewhat sloppy (if a listener is
  232. // registered with registerPriority - not common - between the start
  233. // and end of a single ring drawing - very uncommon - it will be
  234. // called twice).
  235. // TODO: In 3.0, collapse sketch handlers into geometry specific
  236. // drawing controls.
  237. this.control.layer.events.triggerEvent(
  238. "sketchcomplete", {feature : this.polygon}
  239. );
  240. }
  241. },
  242. /**
  243. * Method: destroyFeature
  244. * Destroy temporary geometries
  245. *
  246. * Parameters:
  247. * force - {Boolean} Destroy even if persist is true.
  248. */
  249. destroyFeature: function(force) {
  250. OpenLayers.Handler.Path.prototype.destroyFeature.call(
  251. this, force);
  252. this.polygon = null;
  253. },
  254. /**
  255. * Method: drawFeature
  256. * Render geometries on the temporary layer.
  257. */
  258. drawFeature: function() {
  259. this.layer.drawFeature(this.polygon, this.style);
  260. this.layer.drawFeature(this.point, this.style);
  261. },
  262. /**
  263. * Method: getSketch
  264. * Return the sketch feature.
  265. *
  266. * Returns:
  267. * {<OpenLayers.Feature.Vector>}
  268. */
  269. getSketch: function() {
  270. return this.polygon;
  271. },
  272. /**
  273. * Method: getGeometry
  274. * Return the sketch geometry. If <multi> is true, this will return
  275. * a multi-part geometry.
  276. *
  277. * Returns:
  278. * {<OpenLayers.Geometry.Polygon>}
  279. */
  280. getGeometry: function() {
  281. var geometry = this.polygon && this.polygon.geometry;
  282. if(geometry && this.multi) {
  283. geometry = new OpenLayers.Geometry.MultiPolygon([geometry]);
  284. }
  285. return geometry;
  286. },
  287. CLASS_NAME: "OpenLayers.Handler.Polygon"
  288. });