Project

General

Profile

Download (16.5 KB) Statistics
| Branch: | Tag: | Revision:
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
};
(9-9/35)