1 | import {LatLng, toLatLng} from './LatLng';
|
2 |
|
3 | /*
|
4 | * @class LatLngBounds
|
5 | * @aka L.LatLngBounds
|
6 | *
|
7 | * Represents a rectangular geographical area on a map.
|
8 | *
|
9 | * @example
|
10 | *
|
11 | * ```js
|
12 | * var corner1 = L.latLng(40.712, -74.227),
|
13 | * corner2 = L.latLng(40.774, -74.125),
|
14 | * bounds = L.latLngBounds(corner1, corner2);
|
15 | * ```
|
16 | *
|
17 | * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
|
18 | *
|
19 | * ```js
|
20 | * map.fitBounds([
|
21 | * [40.712, -74.227],
|
22 | * [40.774, -74.125]
|
23 | * ]);
|
24 | * ```
|
25 | *
|
26 | * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
|
27 | *
|
28 | * Note that `LatLngBounds` does not inherit from Leafet's `Class` object,
|
29 | * which means new classes can't inherit from it, and new methods
|
30 | * can't be added to it with the `include` function.
|
31 | */
|
32 |
|
33 | export function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
|
34 | if (!corner1) { return; }
|
35 |
|
36 | var latlngs = corner2 ? [corner1, corner2] : corner1;
|
37 |
|
38 | for (var i = 0, len = latlngs.length; i < len; i++) {
|
39 | this.extend(latlngs[i]);
|
40 | }
|
41 | }
|
42 |
|
43 | LatLngBounds.prototype = {
|
44 |
|
45 | // @method extend(latlng: LatLng): this
|
46 | // Extend the bounds to contain the given point
|
47 |
|
48 | // @alternative
|
49 | // @method extend(otherBounds: LatLngBounds): this
|
50 | // Extend the bounds to contain the given bounds
|
51 | extend: function (obj) {
|
52 | var sw = this._southWest,
|
53 | ne = this._northEast,
|
54 | sw2, ne2;
|
55 |
|
56 | if (obj instanceof LatLng) {
|
57 | sw2 = obj;
|
58 | ne2 = obj;
|
59 |
|
60 | } else if (obj instanceof LatLngBounds) {
|
61 | sw2 = obj._southWest;
|
62 | ne2 = obj._northEast;
|
63 |
|
64 | if (!sw2 || !ne2) { return this; }
|
65 |
|
66 | } else {
|
67 | return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
|
68 | }
|
69 |
|
70 | if (!sw && !ne) {
|
71 | this._southWest = new LatLng(sw2.lat, sw2.lng);
|
72 | this._northEast = new LatLng(ne2.lat, ne2.lng);
|
73 | } else {
|
74 | sw.lat = Math.min(sw2.lat, sw.lat);
|
75 | sw.lng = Math.min(sw2.lng, sw.lng);
|
76 | ne.lat = Math.max(ne2.lat, ne.lat);
|
77 | ne.lng = Math.max(ne2.lng, ne.lng);
|
78 | }
|
79 |
|
80 | return this;
|
81 | },
|
82 |
|
83 | // @method pad(bufferRatio: Number): LatLngBounds
|
84 | // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
|
85 | // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
|
86 | // Negative values will retract the bounds.
|
87 | pad: function (bufferRatio) {
|
88 | var sw = this._southWest,
|
89 | ne = this._northEast,
|
90 | heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
|
91 | widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
|
92 |
|
93 | return new LatLngBounds(
|
94 | new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
|
95 | new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
|
96 | },
|
97 |
|
98 | // @method getCenter(): LatLng
|
99 | // Returns the center point of the bounds.
|
100 | getCenter: function () {
|
101 | return new LatLng(
|
102 | (this._southWest.lat + this._northEast.lat) / 2,
|
103 | (this._southWest.lng + this._northEast.lng) / 2);
|
104 | },
|
105 |
|
106 | // @method getSouthWest(): LatLng
|
107 | // Returns the south-west point of the bounds.
|
108 | getSouthWest: function () {
|
109 | return this._southWest;
|
110 | },
|
111 |
|
112 | // @method getNorthEast(): LatLng
|
113 | // Returns the north-east point of the bounds.
|
114 | getNorthEast: function () {
|
115 | return this._northEast;
|
116 | },
|
117 |
|
118 | // @method getNorthWest(): LatLng
|
119 | // Returns the north-west point of the bounds.
|
120 | getNorthWest: function () {
|
121 | return new LatLng(this.getNorth(), this.getWest());
|
122 | },
|
123 |
|
124 | // @method getSouthEast(): LatLng
|
125 | // Returns the south-east point of the bounds.
|
126 | getSouthEast: function () {
|
127 | return new LatLng(this.getSouth(), this.getEast());
|
128 | },
|
129 |
|
130 | // @method getWest(): Number
|
131 | // Returns the west longitude of the bounds
|
132 | getWest: function () {
|
133 | return this._southWest.lng;
|
134 | },
|
135 |
|
136 | // @method getSouth(): Number
|
137 | // Returns the south latitude of the bounds
|
138 | getSouth: function () {
|
139 | return this._southWest.lat;
|
140 | },
|
141 |
|
142 | // @method getEast(): Number
|
143 | // Returns the east longitude of the bounds
|
144 | getEast: function () {
|
145 | return this._northEast.lng;
|
146 | },
|
147 |
|
148 | // @method getNorth(): Number
|
149 | // Returns the north latitude of the bounds
|
150 | getNorth: function () {
|
151 | return this._northEast.lat;
|
152 | },
|
153 |
|
154 | // @method contains(otherBounds: LatLngBounds): Boolean
|
155 | // Returns `true` if the rectangle contains the given one.
|
156 |
|
157 | // @alternative
|
158 | // @method contains (latlng: LatLng): Boolean
|
159 | // Returns `true` if the rectangle contains the given point.
|
160 | contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
|
161 | if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
|
162 | obj = toLatLng(obj);
|
163 | } else {
|
164 | obj = toLatLngBounds(obj);
|
165 | }
|
166 |
|
167 | var sw = this._southWest,
|
168 | ne = this._northEast,
|
169 | sw2, ne2;
|
170 |
|
171 | if (obj instanceof LatLngBounds) {
|
172 | sw2 = obj.getSouthWest();
|
173 | ne2 = obj.getNorthEast();
|
174 | } else {
|
175 | sw2 = ne2 = obj;
|
176 | }
|
177 |
|
178 | return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
|
179 | (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
|
180 | },
|
181 |
|
182 | // @method intersects(otherBounds: LatLngBounds): Boolean
|
183 | // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
|
184 | intersects: function (bounds) {
|
185 | bounds = toLatLngBounds(bounds);
|
186 |
|
187 | var sw = this._southWest,
|
188 | ne = this._northEast,
|
189 | sw2 = bounds.getSouthWest(),
|
190 | ne2 = bounds.getNorthEast(),
|
191 |
|
192 | latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
|
193 | lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
|
194 |
|
195 | return latIntersects && lngIntersects;
|
196 | },
|
197 |
|
198 | // @method overlaps(otherBounds: Bounds): Boolean
|
199 | // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
|
200 | overlaps: function (bounds) {
|
201 | bounds = toLatLngBounds(bounds);
|
202 |
|
203 | var sw = this._southWest,
|
204 | ne = this._northEast,
|
205 | sw2 = bounds.getSouthWest(),
|
206 | ne2 = bounds.getNorthEast(),
|
207 |
|
208 | latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
|
209 | lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
|
210 |
|
211 | return latOverlaps && lngOverlaps;
|
212 | },
|
213 |
|
214 | // @method toBBoxString(): String
|
215 | // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
|
216 | toBBoxString: function () {
|
217 | return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
|
218 | },
|
219 |
|
220 | // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
|
221 | // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
|
222 | equals: function (bounds, maxMargin) {
|
223 | if (!bounds) { return false; }
|
224 |
|
225 | bounds = toLatLngBounds(bounds);
|
226 |
|
227 | return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
|
228 | this._northEast.equals(bounds.getNorthEast(), maxMargin);
|
229 | },
|
230 |
|
231 | // @method isValid(): Boolean
|
232 | // Returns `true` if the bounds are properly initialized.
|
233 | isValid: function () {
|
234 | return !!(this._southWest && this._northEast);
|
235 | }
|
236 | };
|
237 |
|
238 | // TODO International date line?
|
239 |
|
240 | // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
|
241 | // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
|
242 |
|
243 | // @alternative
|
244 | // @factory L.latLngBounds(latlngs: LatLng[])
|
245 | // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
|
246 | export function toLatLngBounds(a, b) {
|
247 | if (a instanceof LatLngBounds) {
|
248 | return a;
|
249 | }
|
250 | return new LatLngBounds(a, b);
|
251 | }
|