summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
blob: 3b63871382878d7935679c9bebc00c860591e11e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
package com.mapbox.mapboxsdk.geometry;

import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;

import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException;

import java.util.ArrayList;
import java.util.List;

/**
 * A geographical area representing a latitude/longitude aligned rectangle.
 */
public class LatLngBounds implements Parcelable {

    private final double mLatNorth;
    private final double mLatSouth;
    private final double mLonEast;
    private final double mLonWest;

    /**
     * Construct a new LatLngBounds based on its corners, given in NESW
     * order.
     *
     * @param northLatitude Northern Latitude
     * @param eastLongitude Eastern Longitude
     * @param southLatitude Southern Latitude
     * @param westLongitude Western Longitude
     */
    LatLngBounds(final double northLatitude, final double eastLongitude, final double southLatitude, final double westLongitude) {
        this.mLatNorth = northLatitude;
        this.mLonEast = eastLongitude;
        this.mLatSouth = southLatitude;
        this.mLonWest = westLongitude;
    }

    /**
     * Calculates the centerpoint of this LatLngBounds by simple interpolation and returns
     * it as a point. This is a non-geodesic calculation which is not the geographic center.
     *
     * @return LatLng center of this LatLngBounds
     */
    public LatLng getCenter() {
        return new LatLng((this.mLatNorth + this.mLatSouth) / 2,
                (this.mLonEast + this.mLonWest) / 2);
    }

    public double getLatNorth() {
        return this.mLatNorth;
    }

    public double getLatSouth() {
        return this.mLatSouth;
    }

    public double getLonEast() {
        return this.mLonEast;
    }

    public double getLonWest() {
        return this.mLonWest;
    }

    /**
     * Get the area spanned by this LatLngBounds
     *
     * @return LatLngSpan area
     */
    public LatLngSpan getSpan() {
        return new LatLngSpan(getLatitudeSpan(), getLongitudeSpan());
    }

    /**
     * Get the absolute distance, in degrees, between the north and
     * south boundaries of this LatLngBounds
     *
     * @return Span distance
     */
    public double getLatitudeSpan() {
        return Math.abs(this.mLatNorth - this.mLatSouth);
    }

    /**
     * Get the absolute distance, in degrees, between the west and
     * east boundaries of this LatLngBounds
     *
     * @return Span distance
     */
    public double getLongitudeSpan() {
        return Math.abs(this.mLonEast - this.mLonWest);
    }


    /**
     * Validate if LatLngBounds is empty, determined if absolute distance is
     *
     * @return boolean indicating if span is empty
     */
    public boolean isEmptySpan() {
        return getLongitudeSpan() == 0.0 || getLatitudeSpan() == 0.0;
    }

    @Override
    public String toString() {
        return "N:" + this.mLatNorth + "; E:" + this.mLonEast + "; S:" + this.mLatSouth + "; W:" + this.mLonWest;
    }

    /**
     * Constructs a LatLngBounds that contains all of a list of LatLng
     * objects. Empty lists will yield invalid LatLngBounds.
     *
     * @param latLngs List of LatLng objects
     * @return LatLngBounds
     */
    static LatLngBounds fromLatLngs(final List<? extends ILatLng> latLngs) {
        double minLat = 90,
                minLon = 180,
                maxLat = -90,
                maxLon = -180;

        for (final ILatLng gp : latLngs) {
            final double latitude = gp.getLatitude();
            final double longitude = gp.getLongitude();

            minLat = Math.min(minLat, latitude);
            minLon = Math.min(minLon, longitude);
            maxLat = Math.max(maxLat, latitude);
            maxLon = Math.max(maxLon, longitude);
        }

        return new LatLngBounds(maxLat, maxLon, minLat, minLon);
    }

    public LatLng[] toLatLngs() {
        return new LatLng[]{new LatLng(mLatNorth, mLonEast), new LatLng(mLatSouth, mLonWest)};
    }

    /**
     * Determines whether this LatLngBounds matches another one via LatLng.
     *
     * @param o another object
     * @return a boolean indicating whether the LatLngBounds are equal
     */
    @Override
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (o instanceof LatLngBounds) {
            LatLngBounds other = (LatLngBounds) o;
            return mLatNorth == other.getLatNorth()
                    && mLatSouth == other.getLatSouth()
                    && mLonEast == other.getLonEast()
                    && mLonWest == other.getLonWest();
        }
        return false;
    }

    /**
     * Determines whether this LatLngBounds contains a point and the point
     * does not touch its boundary.
     *
     * @param latLng the point which may be contained
     * @return true, if the point is contained within the box.
     */
    public boolean including(final ILatLng latLng) {
        final double latitude = latLng.getLatitude();
        final double longitude = latLng.getLongitude();
        return ((latitude < this.mLatNorth)
                && (latitude > this.mLatSouth))
                && ((longitude < this.mLonEast)
                && (longitude > this.mLonWest));
    }

    /**
     * Returns a new LatLngBounds that stretches to contain both this and another LatLngBounds.
     *
     * @param bounds LatLngBounds to add
     * @return LatLngBounds
     */
    public LatLngBounds union(LatLngBounds bounds) {
        return union(bounds.getLatNorth(), bounds.getLonEast(), bounds.getLatSouth(), bounds.getLonWest());
    }

    /**
     * Returns a new LatLngBounds that stretches to include another LatLngBounds,
     * given by corner points.
     *
     * @param lonNorth Northern Longitude
     * @param latEast  Eastern Latitude
     * @param lonSouth Southern Longitude
     * @param latWest  Western Longitude
     * @return BoundingBox
     */
    public LatLngBounds union(final double lonNorth, final double latEast, final double lonSouth, final double latWest) {
        return new LatLngBounds((this.mLatNorth < lonNorth) ? lonNorth : this.mLatNorth,
                (this.mLonEast < latEast) ? latEast : this.mLonEast,
                (this.mLatSouth > lonSouth) ? lonSouth : this.mLatSouth,
                (this.mLonWest > latWest) ? latWest : this.mLonWest);
    }

    /**
     * Returns a new LatLngBounds that is the intersection of this with another box
     *
     * @param box LatLngBounds to intersect with
     * @return LatLngBounds
     */
    public LatLngBounds intersect(LatLngBounds box) {
        double minLatWest = Math.max(getLonWest(), box.getLonWest());
        double maxLatEast = Math.min(getLonEast(), box.getLonEast());
        if (maxLatEast > minLatWest) {
            double minLonSouth = Math.max(getLatSouth(), box.getLatSouth());
            double maxLonNorth = Math.min(getLatNorth(), box.getLatNorth());
            if (maxLonNorth > minLonSouth) {
                return new LatLngBounds(maxLonNorth, maxLatEast, minLonSouth, minLatWest);
            }
        }
        return null;
    }

    /**
     * Returns a new LatLngBounds that is the intersection of this with another LatLngBounds
     *
     * @param northLatitude Northern Longitude
     * @param eastLongitude Eastern Latitude
     * @param southLatitude Southern Longitude
     * @param westLongitude Western Latitude
     * @return LatLngBounds
     */
    public LatLngBounds intersect(double northLatitude, double eastLongitude, double southLatitude, double westLongitude) {
        return intersect(new LatLngBounds(northLatitude, eastLongitude, southLatitude, westLongitude));
    }

    public static final Parcelable.Creator<LatLngBounds> CREATOR =
            new Parcelable.Creator<LatLngBounds>() {
                @Override
                public LatLngBounds createFromParcel(final Parcel in) {
                    return readFromParcel(in);
                }

                @Override
                public LatLngBounds[] newArray(final int size) {
                    return new LatLngBounds[size];
                }
            };

    @Override
    public int hashCode() {
        return (int) ((mLatNorth + 90)
                + ((mLatSouth + 90) * 1000)
                + ((mLonEast + 180) * 1000000)
                + ((mLonEast + 180) * 1000000000));
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(final Parcel out, final int arg1) {
        out.writeDouble(this.mLatNorth);
        out.writeDouble(this.mLonEast);
        out.writeDouble(this.mLatSouth);
        out.writeDouble(this.mLonWest);
    }

    private static LatLngBounds readFromParcel(final Parcel in) {
        final double lonNorth = in.readDouble();
        final double latEast = in.readDouble();
        final double lonSouth = in.readDouble();
        final double latWest = in.readDouble();
        return new LatLngBounds(lonNorth, latEast, lonSouth, latWest);
    }

    /**
     * Builder for composing LatLngBounds objects.
     */
    public static final class Builder {

        private List<LatLng> mLatLngList;

        public Builder() {
            mLatLngList = new ArrayList<>();
        }

        public LatLngBounds build() {
            if (mLatLngList.size() < 2) {
                throw new InvalidLatLngBoundsException(mLatLngList.size());
            }
            return LatLngBounds.fromLatLngs(mLatLngList);
        }

        public Builder include(@NonNull LatLng latLng) {
            mLatLngList.add(latLng);
            return this;
        }
    }
}