Geometry.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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/BaseTypes/Class.js
  7. * @requires OpenLayers/Format/WKT.js
  8. * @requires OpenLayers/Feature/Vector.js
  9. */
  10. /**
  11. * Class: OpenLayers.Geometry
  12. * A Geometry is a description of a geographic object. Create an instance of
  13. * this class with the <OpenLayers.Geometry> constructor. This is a base class,
  14. * typical geometry types are described by subclasses of this class.
  15. */
  16. OpenLayers.Geometry = OpenLayers.Class({
  17. /**
  18. * Property: id
  19. * {String} A unique identifier for this geometry.
  20. */
  21. id: null,
  22. /**
  23. * Property: parent
  24. * {<OpenLayers.Geometry>}This is set when a Geometry is added as component
  25. * of another geometry
  26. */
  27. parent: null,
  28. /**
  29. * Property: bounds
  30. * {<OpenLayers.Bounds>} The bounds of this geometry
  31. */
  32. bounds: null,
  33. /**
  34. * Constructor: OpenLayers.Geometry
  35. * Creates a geometry object.
  36. */
  37. initialize: function() {
  38. this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_");
  39. },
  40. /**
  41. * Method: destroy
  42. * Destroy this geometry.
  43. */
  44. destroy: function() {
  45. this.id = null;
  46. this.bounds = null;
  47. },
  48. /**
  49. * APIMethod: clone
  50. * Create a clone of this geometry. Does not set any non-standard
  51. * properties of the cloned geometry.
  52. *
  53. * Returns:
  54. * {<OpenLayers.Geometry>} An exact clone of this geometry.
  55. */
  56. clone: function() {
  57. return new OpenLayers.Geometry();
  58. },
  59. /**
  60. * Set the bounds for this Geometry.
  61. *
  62. * Parameters:
  63. * object - {<OpenLayers.Bounds>}
  64. */
  65. setBounds: function(bounds) {
  66. if (bounds) {
  67. this.bounds = bounds.clone();
  68. }
  69. },
  70. /**
  71. * Method: clearBounds
  72. * Nullify this components bounds and that of its parent as well.
  73. */
  74. clearBounds: function() {
  75. this.bounds = null;
  76. if (this.parent) {
  77. this.parent.clearBounds();
  78. }
  79. },
  80. /**
  81. * Method: extendBounds
  82. * Extend the existing bounds to include the new bounds.
  83. * If geometry's bounds is not yet set, then set a new Bounds.
  84. *
  85. * Parameters:
  86. * newBounds - {<OpenLayers.Bounds>}
  87. */
  88. extendBounds: function(newBounds){
  89. var bounds = this.getBounds();
  90. if (!bounds) {
  91. this.setBounds(newBounds);
  92. } else {
  93. this.bounds.extend(newBounds);
  94. }
  95. },
  96. /**
  97. * APIMethod: getBounds
  98. * Get the bounds for this Geometry. If bounds is not set, it
  99. * is calculated again, this makes queries faster.
  100. *
  101. * Returns:
  102. * {<OpenLayers.Bounds>}
  103. */
  104. getBounds: function() {
  105. if (this.bounds == null) {
  106. this.calculateBounds();
  107. }
  108. return this.bounds;
  109. },
  110. /**
  111. * APIMethod: calculateBounds
  112. * Recalculate the bounds for the geometry.
  113. */
  114. calculateBounds: function() {
  115. //
  116. // This should be overridden by subclasses.
  117. //
  118. },
  119. /**
  120. * APIMethod: distanceTo
  121. * Calculate the closest distance between two geometries (on the x-y plane).
  122. *
  123. * Parameters:
  124. * geometry - {<OpenLayers.Geometry>} The target geometry.
  125. * options - {Object} Optional properties for configuring the distance
  126. * calculation.
  127. *
  128. * Valid options depend on the specific geometry type.
  129. *
  130. * Returns:
  131. * {Number | Object} The distance between this geometry and the target.
  132. * If details is true, the return will be an object with distance,
  133. * x0, y0, x1, and x2 properties. The x0 and y0 properties represent
  134. * the coordinates of the closest point on this geometry. The x1 and y1
  135. * properties represent the coordinates of the closest point on the
  136. * target geometry.
  137. */
  138. distanceTo: function(geometry, options) {
  139. },
  140. /**
  141. * APIMethod: getVertices
  142. * Return a list of all points in this geometry.
  143. *
  144. * Parameters:
  145. * nodes - {Boolean} For lines, only return vertices that are
  146. * endpoints. If false, for lines, only vertices that are not
  147. * endpoints will be returned. If not provided, all vertices will
  148. * be returned.
  149. *
  150. * Returns:
  151. * {Array} A list of all vertices in the geometry.
  152. */
  153. getVertices: function(nodes) {
  154. },
  155. /**
  156. * Method: atPoint
  157. * Note - This is only an approximation based on the bounds of the
  158. * geometry.
  159. *
  160. * Parameters:
  161. * lonlat - {<OpenLayers.LonLat>}
  162. * toleranceLon - {float} Optional tolerance in Geometric Coords
  163. * toleranceLat - {float} Optional tolerance in Geographic Coords
  164. *
  165. * Returns:
  166. * {Boolean} Whether or not the geometry is at the specified location
  167. */
  168. atPoint: function(lonlat, toleranceLon, toleranceLat) {
  169. var atPoint = false;
  170. var bounds = this.getBounds();
  171. if ((bounds != null) && (lonlat != null)) {
  172. var dX = (toleranceLon != null) ? toleranceLon : 0;
  173. var dY = (toleranceLat != null) ? toleranceLat : 0;
  174. var toleranceBounds =
  175. new OpenLayers.Bounds(this.bounds.left - dX,
  176. this.bounds.bottom - dY,
  177. this.bounds.right + dX,
  178. this.bounds.top + dY);
  179. atPoint = toleranceBounds.containsLonLat(lonlat);
  180. }
  181. return atPoint;
  182. },
  183. /**
  184. * Method: getLength
  185. * Calculate the length of this geometry. This method is defined in
  186. * subclasses.
  187. *
  188. * Returns:
  189. * {Float} The length of the collection by summing its parts
  190. */
  191. getLength: function() {
  192. //to be overridden by geometries that actually have a length
  193. //
  194. return 0.0;
  195. },
  196. /**
  197. * Method: getArea
  198. * Calculate the area of this geometry. This method is defined in subclasses.
  199. *
  200. * Returns:
  201. * {Float} The area of the collection by summing its parts
  202. */
  203. getArea: function() {
  204. //to be overridden by geometries that actually have an area
  205. //
  206. return 0.0;
  207. },
  208. /**
  209. * APIMethod: getCentroid
  210. * Calculate the centroid of this geometry. This method is defined in subclasses.
  211. *
  212. * Returns:
  213. * {<OpenLayers.Geometry.Point>} The centroid of the collection
  214. */
  215. getCentroid: function() {
  216. return null;
  217. },
  218. /**
  219. * Method: toString
  220. * Returns the Well-Known Text representation of a geometry
  221. *
  222. * Returns:
  223. * {String} Well-Known Text
  224. */
  225. toString: function() {
  226. return OpenLayers.Format.WKT.prototype.write(
  227. new OpenLayers.Feature.Vector(this)
  228. );
  229. },
  230. CLASS_NAME: "OpenLayers.Geometry"
  231. });
  232. /**
  233. * Function: OpenLayers.Geometry.fromWKT
  234. * Generate a geometry given a Well-Known Text string.
  235. *
  236. * Parameters:
  237. * wkt - {String} A string representing the geometry in Well-Known Text.
  238. *
  239. * Returns:
  240. * {<OpenLayers.Geometry>} A geometry of the appropriate class.
  241. */
  242. OpenLayers.Geometry.fromWKT = function(wkt) {
  243. var format = arguments.callee.format;
  244. if(!format) {
  245. format = new OpenLayers.Format.WKT();
  246. arguments.callee.format = format;
  247. }
  248. var geom;
  249. var result = format.read(wkt);
  250. if(result instanceof OpenLayers.Feature.Vector) {
  251. geom = result.geometry;
  252. } else if(OpenLayers.Util.isArray(result)) {
  253. var len = result.length;
  254. var components = new Array(len);
  255. for(var i=0; i<len; ++i) {
  256. components[i] = result[i].geometry;
  257. }
  258. geom = new OpenLayers.Geometry.Collection(components);
  259. }
  260. return geom;
  261. };
  262. /**
  263. * Method: OpenLayers.Geometry.segmentsIntersect
  264. * Determine whether two line segments intersect. Optionally calculates
  265. * and returns the intersection point. This function is optimized for
  266. * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those
  267. * obvious cases where there is no intersection, the function should
  268. * not be called.
  269. *
  270. * Parameters:
  271. * seg1 - {Object} Object representing a segment with properties x1, y1, x2,
  272. * and y2. The start point is represented by x1 and y1. The end point
  273. * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
  274. * seg2 - {Object} Object representing a segment with properties x1, y1, x2,
  275. * and y2. The start point is represented by x1 and y1. The end point
  276. * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
  277. * options - {Object} Optional properties for calculating the intersection.
  278. *
  279. * Valid options:
  280. * point - {Boolean} Return the intersection point. If false, the actual
  281. * intersection point will not be calculated. If true and the segments
  282. * intersect, the intersection point will be returned. If true and
  283. * the segments do not intersect, false will be returned. If true and
  284. * the segments are coincident, true will be returned.
  285. * tolerance - {Number} If a non-null value is provided, if the segments are
  286. * within the tolerance distance, this will be considered an intersection.
  287. * In addition, if the point option is true and the calculated intersection
  288. * is within the tolerance distance of an end point, the endpoint will be
  289. * returned instead of the calculated intersection. Further, if the
  290. * intersection is within the tolerance of endpoints on both segments, or
  291. * if two segment endpoints are within the tolerance distance of eachother
  292. * (but no intersection is otherwise calculated), an endpoint on the
  293. * first segment provided will be returned.
  294. *
  295. * Returns:
  296. * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.
  297. * If the point argument is true, the return will be the intersection
  298. * point or false if none exists. If point is true and the segments
  299. * are coincident, return will be true (and the instersection is equal
  300. * to the shorter segment).
  301. */
  302. OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {
  303. var point = options && options.point;
  304. var tolerance = options && options.tolerance;
  305. var intersection = false;
  306. var x11_21 = seg1.x1 - seg2.x1;
  307. var y11_21 = seg1.y1 - seg2.y1;
  308. var x12_11 = seg1.x2 - seg1.x1;
  309. var y12_11 = seg1.y2 - seg1.y1;
  310. var y22_21 = seg2.y2 - seg2.y1;
  311. var x22_21 = seg2.x2 - seg2.x1;
  312. var d = (y22_21 * x12_11) - (x22_21 * y12_11);
  313. var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
  314. var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
  315. if(d == 0) {
  316. // parallel
  317. if(n1 == 0 && n2 == 0) {
  318. // coincident
  319. intersection = true;
  320. }
  321. } else {
  322. var along1 = n1 / d;
  323. var along2 = n2 / d;
  324. if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
  325. // intersect
  326. if(!point) {
  327. intersection = true;
  328. } else {
  329. // calculate the intersection point
  330. var x = seg1.x1 + (along1 * x12_11);
  331. var y = seg1.y1 + (along1 * y12_11);
  332. intersection = new OpenLayers.Geometry.Point(x, y);
  333. }
  334. }
  335. }
  336. if(tolerance) {
  337. var dist;
  338. if(intersection) {
  339. if(point) {
  340. var segs = [seg1, seg2];
  341. var seg, x, y;
  342. // check segment endpoints for proximity to intersection
  343. // set intersection to first endpoint within the tolerance
  344. outer: for(var i=0; i<2; ++i) {
  345. seg = segs[i];
  346. for(var j=1; j<3; ++j) {
  347. x = seg["x" + j];
  348. y = seg["y" + j];
  349. dist = Math.sqrt(
  350. Math.pow(x - intersection.x, 2) +
  351. Math.pow(y - intersection.y, 2)
  352. );
  353. if(dist < tolerance) {
  354. intersection.x = x;
  355. intersection.y = y;
  356. break outer;
  357. }
  358. }
  359. }
  360. }
  361. } else {
  362. // no calculated intersection, but segments could be within
  363. // the tolerance of one another
  364. var segs = [seg1, seg2];
  365. var source, target, x, y, p, result;
  366. // check segment endpoints for proximity to intersection
  367. // set intersection to first endpoint within the tolerance
  368. outer: for(var i=0; i<2; ++i) {
  369. source = segs[i];
  370. target = segs[(i+1)%2];
  371. for(var j=1; j<3; ++j) {
  372. p = {x: source["x"+j], y: source["y"+j]};
  373. result = OpenLayers.Geometry.distanceToSegment(p, target);
  374. if(result.distance < tolerance) {
  375. if(point) {
  376. intersection = new OpenLayers.Geometry.Point(p.x, p.y);
  377. } else {
  378. intersection = true;
  379. }
  380. break outer;
  381. }
  382. }
  383. }
  384. }
  385. }
  386. return intersection;
  387. };
  388. /**
  389. * Function: OpenLayers.Geometry.distanceToSegment
  390. *
  391. * Parameters:
  392. * point - {Object} An object with x and y properties representing the
  393. * point coordinates.
  394. * segment - {Object} An object with x1, y1, x2, and y2 properties
  395. * representing endpoint coordinates.
  396. *
  397. * Returns:
  398. * {Object} An object with distance, x, and y properties. The distance
  399. * will be the shortest distance between the input point and segment.
  400. * The x and y properties represent the coordinates along the segment
  401. * where the shortest distance meets the segment.
  402. */
  403. OpenLayers.Geometry.distanceToSegment = function(point, segment) {
  404. var x0 = point.x;
  405. var y0 = point.y;
  406. var x1 = segment.x1;
  407. var y1 = segment.y1;
  408. var x2 = segment.x2;
  409. var y2 = segment.y2;
  410. var dx = x2 - x1;
  411. var dy = y2 - y1;
  412. var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
  413. (Math.pow(dx, 2) + Math.pow(dy, 2));
  414. var x, y;
  415. if(along <= 0.0) {
  416. x = x1;
  417. y = y1;
  418. } else if(along >= 1.0) {
  419. x = x2;
  420. y = y2;
  421. } else {
  422. x = x1 + along * dx;
  423. y = y1 + along * dy;
  424. }
  425. return {
  426. distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)),
  427. x: x, y: y
  428. };
  429. };