Polygon.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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/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. * Property: 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. /**
  59. * Method: createFeature
  60. * Add temporary geometries
  61. *
  62. * Parameters:
  63. * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
  64. * feature.
  65. */
  66. createFeature: function(pixel) {
  67. var lonlat = this.layer.getLonLatFromViewPortPx(pixel);
  68. var geometry = new OpenLayers.Geometry.Point(
  69. lonlat.lon, lonlat.lat
  70. );
  71. this.point = new OpenLayers.Feature.Vector(geometry);
  72. this.line = new OpenLayers.Feature.Vector(
  73. new OpenLayers.Geometry.LinearRing([this.point.geometry])
  74. );
  75. this.polygon = new OpenLayers.Feature.Vector(
  76. new OpenLayers.Geometry.Polygon([this.line.geometry])
  77. );
  78. this.callback("create", [this.point.geometry, this.getSketch()]);
  79. this.point.geometry.clearBounds();
  80. this.layer.addFeatures([this.polygon, this.point], {silent: true});
  81. },
  82. /**
  83. * Method: addPoint
  84. * Add point to geometry.
  85. *
  86. * Parameters:
  87. * pixel - {<OpenLayers.Pixel>} The pixel location for the new point.
  88. */
  89. addPoint: function(pixel) {
  90. if(!this.drawingHole && this.holeModifier &&
  91. this.evt && this.evt[this.holeModifier]) {
  92. var geometry = this.point.geometry;
  93. var features = this.control.layer.features;
  94. var candidate, polygon;
  95. // look for intersections, last drawn gets priority
  96. for (var i=features.length-1; i>=0; --i) {
  97. candidate = features[i].geometry;
  98. if ((candidate instanceof OpenLayers.Geometry.Polygon ||
  99. candidate instanceof OpenLayers.Geometry.MultiPolygon) &&
  100. candidate.intersects(geometry)) {
  101. polygon = features[i];
  102. this.control.layer.removeFeatures([polygon], {silent: true});
  103. this.control.layer.events.registerPriority(
  104. "sketchcomplete", this, this.finalizeInteriorRing
  105. );
  106. this.control.layer.events.registerPriority(
  107. "sketchmodified", this, this.enforceTopology
  108. );
  109. polygon.geometry.addComponent(this.line.geometry);
  110. this.polygon = polygon;
  111. this.drawingHole = true;
  112. break;
  113. }
  114. }
  115. }
  116. OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments);
  117. },
  118. /**
  119. * Method: getCurrentPointIndex
  120. *
  121. * Returns:
  122. * {Number} The index of the most recently drawn point.
  123. */
  124. getCurrentPointIndex: function() {
  125. return this.line.geometry.components.length - 2;
  126. },
  127. /**
  128. * Method: enforceTopology
  129. * Simple topology enforcement for drawing interior rings. Ensures vertices
  130. * of interior rings are contained by exterior ring. Other topology
  131. * rules are enforced in <finalizeInteriorRing> to allow drawing of
  132. * rings that intersect only during the sketch (e.g. a "C" shaped ring
  133. * that nearly encloses another ring).
  134. */
  135. enforceTopology: function(event) {
  136. var point = event.vertex;
  137. var components = this.line.geometry.components;
  138. // ensure that vertices of interior ring are contained by exterior ring
  139. if (!this.polygon.geometry.intersects(point)) {
  140. var last = components[components.length-3];
  141. point.x = last.x;
  142. point.y = last.y;
  143. }
  144. },
  145. /**
  146. * Method: finishGeometry
  147. * Finish the geometry and send it back to the control.
  148. */
  149. finishGeometry: function() {
  150. var index = this.line.geometry.components.length - 2;
  151. this.line.geometry.removeComponent(this.line.geometry.components[index]);
  152. this.removePoint();
  153. this.finalize();
  154. },
  155. /**
  156. * Method: finalizeInteriorRing
  157. * Enforces that new ring has some area and doesn't contain vertices of any
  158. * other rings.
  159. */
  160. finalizeInteriorRing: function() {
  161. var ring = this.line.geometry;
  162. // ensure that ring has some area
  163. var modified = (ring.getArea() !== 0);
  164. if (modified) {
  165. // ensure that new ring doesn't intersect any other rings
  166. var rings = this.polygon.geometry.components;
  167. for (var i=rings.length-2; i>=0; --i) {
  168. if (ring.intersects(rings[i])) {
  169. modified = false;
  170. break;
  171. }
  172. }
  173. if (modified) {
  174. // ensure that new ring doesn't contain any other rings
  175. var target;
  176. outer: for (var i=rings.length-2; i>0; --i) {
  177. var points = rings[i].components;
  178. for (var j=0, jj=points.length; j<jj; ++j) {
  179. if (ring.containsPoint(points[j])) {
  180. modified = false;
  181. break outer;
  182. }
  183. }
  184. }
  185. }
  186. }
  187. if (modified) {
  188. if (this.polygon.state !== OpenLayers.State.INSERT) {
  189. this.polygon.state = OpenLayers.State.UPDATE;
  190. }
  191. } else {
  192. this.polygon.geometry.removeComponent(ring);
  193. }
  194. this.restoreFeature();
  195. return false;
  196. },
  197. /**
  198. * APIMethod: cancel
  199. * Finish the geometry and call the "cancel" callback.
  200. */
  201. cancel: function() {
  202. if (this.drawingHole) {
  203. this.polygon.geometry.removeComponent(this.line.geometry);
  204. this.restoreFeature(true);
  205. }
  206. return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments);
  207. },
  208. /**
  209. * Method: restoreFeature
  210. * Move the feature from the sketch layer to the target layer.
  211. *
  212. * Properties:
  213. * cancel - {Boolean} Cancel drawing. If falsey, the "sketchcomplete" event
  214. * will be fired.
  215. */
  216. restoreFeature: function(cancel) {
  217. this.control.layer.events.unregister(
  218. "sketchcomplete", this, this.finalizeInteriorRing
  219. );
  220. this.control.layer.events.unregister(
  221. "sketchmodified", this, this.enforceTopology
  222. );
  223. this.layer.removeFeatures([this.polygon], {silent: true});
  224. this.control.layer.addFeatures([this.polygon], {silent: true});
  225. this.drawingHole = false;
  226. if (!cancel) {
  227. // Re-trigger "sketchcomplete" so other listeners can do their
  228. // business. While this is somewhat sloppy (if a listener is
  229. // registered with registerPriority - not common - between the start
  230. // and end of a single ring drawing - very uncommon - it will be
  231. // called twice).
  232. // TODO: In 3.0, collapse sketch handlers into geometry specific
  233. // drawing controls.
  234. this.control.layer.events.triggerEvent(
  235. "sketchcomplete", {feature : this.polygon}
  236. );
  237. }
  238. },
  239. /**
  240. * Method: destroyFeature
  241. * Destroy temporary geometries
  242. *
  243. * Parameters:
  244. * force - {Boolean} Destroy even if persist is true.
  245. */
  246. destroyFeature: function(force) {
  247. OpenLayers.Handler.Path.prototype.destroyFeature.call(
  248. this, force);
  249. this.polygon = null;
  250. },
  251. /**
  252. * Method: drawFeature
  253. * Render geometries on the temporary layer.
  254. */
  255. drawFeature: function() {
  256. this.layer.drawFeature(this.polygon, this.style);
  257. this.layer.drawFeature(this.point, this.style);
  258. },
  259. /**
  260. * Method: getSketch
  261. * Return the sketch feature.
  262. *
  263. * Returns:
  264. * {<OpenLayers.Feature.Vector>}
  265. */
  266. getSketch: function() {
  267. return this.polygon;
  268. },
  269. /**
  270. * Method: getGeometry
  271. * Return the sketch geometry. If <multi> is true, this will return
  272. * a multi-part geometry.
  273. *
  274. * Returns:
  275. * {<OpenLayers.Geometry.Polygon>}
  276. */
  277. getGeometry: function() {
  278. var geometry = this.polygon && this.polygon.geometry;
  279. if(geometry && this.multi) {
  280. geometry = new OpenLayers.Geometry.MultiPolygon([geometry]);
  281. }
  282. return geometry;
  283. },
  284. CLASS_NAME: "OpenLayers.Handler.Polygon"
  285. });