blob: b00446a542b481527b7a42dd10ff6baae4b4f9d0 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart'
show immutable, objectRuntimeType, visibleForTesting;
/// A pair of latitude and longitude coordinates, stored as degrees.
@immutable
class LatLng {
/// Creates a geographical location specified in degrees [latitude] and
/// [longitude].
///
/// The latitude is clamped to the inclusive interval from -90.0 to +90.0.
///
/// The longitude is normalized to the half-open interval from -180.0
/// (inclusive) to +180.0 (exclusive).
const LatLng(double latitude, double longitude)
: latitude =
latitude < -90.0 ? -90.0 : (90.0 < latitude ? 90.0 : latitude),
// Avoids normalization if possible to prevent unnecessary loss of precision
longitude = longitude >= -180 && longitude < 180
? longitude
: (longitude + 180.0) % 360.0 - 180.0;
/// The latitude in degrees between -90.0 and 90.0, both inclusive.
final double latitude;
/// The longitude in degrees between -180.0 (inclusive) and 180.0 (exclusive).
final double longitude;
/// Converts this object to something serializable in JSON.
Object toJson() {
return <double>[latitude, longitude];
}
/// Initialize a LatLng from an \[lat, lng\] array.
static LatLng? fromJson(Object? json) {
if (json == null) {
return null;
}
assert(json is List && json.length == 2);
final List<Object?> list = json as List<Object?>;
return LatLng(list[0]! as double, list[1]! as double);
}
@override
String toString() =>
'${objectRuntimeType(this, 'LatLng')}($latitude, $longitude)';
@override
bool operator ==(Object other) {
return other is LatLng &&
other.latitude == latitude &&
other.longitude == longitude;
}
@override
int get hashCode => Object.hash(latitude, longitude);
}
/// A latitude/longitude aligned rectangle.
///
/// The rectangle conceptually includes all points (lat, lng) where
/// * lat ∈ [`southwest.latitude`, `northeast.latitude`]
/// * lng ∈ [`southwest.longitude`, `northeast.longitude`],
/// if `southwest.longitude` ≤ `northeast.longitude`,
/// * lng ∈ [-180, `northeast.longitude`] ∪ [`southwest.longitude`, 180],
/// if `northeast.longitude` < `southwest.longitude`
@immutable
class LatLngBounds {
/// Creates geographical bounding box with the specified corners.
///
/// The latitude of the southwest corner cannot be larger than the
/// latitude of the northeast corner.
LatLngBounds({required this.southwest, required this.northeast})
: assert(southwest.latitude <= northeast.latitude);
/// The southwest corner of the rectangle.
final LatLng southwest;
/// The northeast corner of the rectangle.
final LatLng northeast;
/// Converts this object to something serializable in JSON.
Object toJson() {
return <Object>[southwest.toJson(), northeast.toJson()];
}
/// Returns whether this rectangle contains the given [LatLng].
bool contains(LatLng point) {
return _containsLatitude(point.latitude) &&
_containsLongitude(point.longitude);
}
bool _containsLatitude(double lat) {
return (southwest.latitude <= lat) && (lat <= northeast.latitude);
}
bool _containsLongitude(double lng) {
if (southwest.longitude <= northeast.longitude) {
return southwest.longitude <= lng && lng <= northeast.longitude;
} else {
return southwest.longitude <= lng || lng <= northeast.longitude;
}
}
/// Converts a list to [LatLngBounds].
@visibleForTesting
static LatLngBounds? fromList(Object? json) {
if (json == null) {
return null;
}
assert(json is List && json.length == 2);
final List<Object?> list = json as List<Object?>;
return LatLngBounds(
southwest: LatLng.fromJson(list[0])!,
northeast: LatLng.fromJson(list[1])!,
);
}
@override
String toString() {
return '${objectRuntimeType(this, 'LatLngBounds')}($southwest, $northeast)';
}
@override
bool operator ==(Object other) {
return other is LatLngBounds &&
other.southwest == southwest &&
other.northeast == northeast;
}
@override
int get hashCode => Object.hash(southwest, northeast);
}