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