Commit 13634cfd by Rizal Hermawan

Initial commit

parents
# Files and directories created by pub
.dart_tool/
.packages
# Conventional directory for build outputs
build/
# Directory created by dartdoc
doc/api/
## [0.9.0]
- Change to const constructor for `LatLng`.
- remove `setLatitude()` and `setLongitude()`.
- Bump minimum dart version to 3.0.
## [0.8.2]
- Sexagesimal fixes and utils
- Upgrade dependencies
## [0.8.1]
- Add GeoJSON compliant toJson() and fromJson().
## [0.8.0]
- Use pedantic.
- camelCase constants.
- Add example.
- Other lint fixes.
## [0.7.0]
- Support null safety, forked from the original repo, which is now archived.
- Address https://github.com/MikeMitterer/dart-latlong/issues/1 and issue 2.
For previous releases, see https://pub.dev/packages/latlong/changelog.
Copyright 2015 Michael Mitterer (office@mikemitterer.at),
IT-Consulting and Development Limited, Austrian Branch
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language
governing permissions and limitations under the License.
This is a fork of https://github.com/MikeMitterer/dart-latlong. The goal is to
keep it up to date with Dart language changes.
## LatLong provides a lightweight library for common latitude and longitude calculation.
This library supports both, the "Haversine" and the "Vincenty" algorithm.
"Haversine" is a bit faster but "Vincenty" is far more accurate!
<p align="center">
<img alt="LatLong" src="https://github.com/MikeMitterer/dart-latlong/raw/master/doc/images/latlong.jpg">
</p>
[Catmull-Rom algorithm](https://hawkesy.blogspot.co.at/2010/05/catmull-rom-spline-curve-implementation.html)
is used for smoothing out the path.
## Basic usage
### Distance
```dart
final Distance distance = new Distance();
// km = 423
final int km = distance.as(LengthUnit.Kilometer,
new LatLng(52.518611,13.408056),new LatLng(51.519475,7.46694444));
// meter = 422591.551
final int meter = distance(
new LatLng(52.518611,13.408056),
new LatLng(51.519475,7.46694444)
);
```
## Offset
```dart
final Distance distance = const Distance();
final num distanceInMeter = (earthRadius * math.pi / 4).round();
final p1 = new LatLng(0.0, 0.0);
final p2 = distance.offset(p1, distanceInMeter, 180);
// LatLng(latitude:-45.219848, longitude:0.0)
print(p2.round());
// 45° 13' 11.45" S, 0° 0' 0.00" O
print(p2.toSexagesimal());
```
## Path smoothing
```dart
// zigzag is a list of coordinates
final Path path = new Path.from(zigzag);
// Result is below
final Path steps = path.equalize(8,smoothPath: true);
```
<p align="center">
<img alt="Smooth path" src="https://github.com/MikeMitterer/dart-latlong/raw/master/doc/images/smooth-path.jpg">
</p>
## Features and bugs
Please file feature requests and bugs at the
[issue tracker](https://github.com/MikeMitterer/dart-latlong/issues).
## License
Copyright 2015 Michael Mitterer (office@mikemitterer.at),
IT-Consulting and Development Limited, Austrian Branch
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language
governing permissions and limitations under the License.
If this plugin is helpful for you - please
[(Circle)](https://gplus.mikemitterer.at/) me or **star** this repo here on
GitHub
# Test the VM by default.
platforms: [ vm ]
paths:
- test/unit
import 'package:latlong2/latlong.dart';
const EARTH_RADIUS = 6371000.0;
void main() {
var distance = Distance();
// km = 423
final km = distance.as(LengthUnit.Kilometer, LatLng(52.518611, 13.408056),
LatLng(51.519475, 7.46694444));
// meter = 422591.551
final meter =
distance(LatLng(52.518611, 13.408056), LatLng(51.519475, 7.46694444));
print('km: $km, meter: $meter');
distance = const Distance();
final num distanceInMeter = (earthRadius * pi / 4).round();
final p1 = LatLng(0.0, 0.0);
final p2 = distance.offset(p1, distanceInMeter, 180);
// LatLng(latitude:-45.219848, longitude:0.0)
print(p2.round());
// 45° 13' 11.45" S, 0° 0' 0.00" O
print(p2.toSexagesimal());
//create a new distance calculator with Haversine algorithm
distance = const Distance(calculator: Haversine());
//create coordinates with NaN or Infinity state to check if the distance is calculated correctly
final point1 = LatLng(double.nan, 0.0);
final point2 = distance.offset(point1, distanceInMeter, 180);
var meterDistance = distance.as(LengthUnit.Meter,point1, point2);
print(meterDistance);
}
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// Helps with latitude / longitude calculations.
///
/// For distance calculations the default algorithm [Vincenty] is used.
/// [Vincenty] is a bit slower than [Haversine] but fare more accurate!
///
/// final Distance distance = new Distance();
///
/// // km = 423
/// final int km = distance.as(LengthUnit.Kilometer,
/// new LatLng(52.518611,13.408056),new LatLng(51.519475,7.46694444));
///
/// // meter = 422592
/// final int meter = distance(new LatLng(52.518611,13.408056),new LatLng(51.519475,7.46694444));
///
/// Find more infos on [Movable Type Scripts](http://www.movable-type.co.uk/scripts/latlong2.html)
/// and [Movable Type Scripts - Vincenty](http://www.movable-type.co.uk/scripts/latlong2-vincenty.html)
///
/// ![latlong2](http://eogn.com/images/newsletter/2014/Latitude-and-longitude.png)
///
/// ![Map](http://www.isobudgets.com/wp-content/uploads/2014/03/latitude-longitude.jpg)
///
library latlong2;
import 'dart:math' as math;
import 'package:latlong2/spline.dart';
import 'package:intl/intl.dart';
part 'latlong/interfaces.dart';
part 'latlong/calculator/Haversine.dart';
part 'latlong/calculator/Vincenty.dart';
part 'latlong/Distance.dart';
part 'latlong/LatLng.dart';
part 'latlong/LengthUnit.dart';
part 'latlong/Path.dart';
part 'latlong/Circle.dart';
/// Equator radius in meter (WGS84 ellipsoid)
const double equatorRadius = 6378137.0;
/// Polar radius in meter (WGS84 ellipsoid)
const double polarRadius = 6356752.314245;
/// WGS84
const double flattening = 1 / 298.257223563;
/// Earth radius in meter
const double earthRadius = equatorRadius;
/// The PI constant.
const double pi = math.pi;
/// Converts degree to radian
double degToRadian(final double deg) => deg * (pi / 180.0);
/// Radian to degree
double radianToDeg(final double rad) => rad * (180.0 / pi);
/// Rounds [value] to given number of [decimals]
double round(final double value, {final int decimals = 6}) =>
(value * math.pow(10, decimals)).round() / math.pow(10, decimals);
/// Convert a bearing to be within the 0 to +360 degrees range.
/// Compass bearing is in the rangen 0° ... 360°
double normalizeBearing(final double bearing) => (bearing + 360) % 360;
/// Converts a decimal coordinate value to sexagesimal format
///
/// final String sexa1 = decimal2sexagesimal(51.519475);
/// expect(sexa1, '51° 31\' 10.11"');
///
/// final String sexa2 = decimal2sexagesimal(-42.883891);
/// expect(sexa2, '42° 53\' 02.01"');
///
String decimal2sexagesimal(final double dec) {
final buf = StringBuffer();
final absDec = dec.abs();
final deg = absDec.floor();
buf.write(deg.toString() + '°');
final mins = (absDec - deg) * 60.0;
final min = mins.floor();
buf.write(' ' + zeroPad(min) + "'");
final secs = (mins - mins.floorToDouble()) * 60.0;
final sec = secs.floor();
final frac = ((secs - secs.floorToDouble()) * 100.0).round();
buf.write(' ' + zeroPad(sec) + '.' + zeroPad(frac) + '"');
return buf.toString();
}
/// Converts a string coordinate value in sexagesimal format to decimal
///
/// final dec1 = sexagesimal2decimal('51° 31\' 10.11"');
/// expect(dec1, 51.519475);
/// final dec2 = sexagesimal2decimal('19° 23\' 32.00"');
/// expect(dec2, 19.392222222222223);
///
double sexagesimal2decimal(final String str) {
final pattern = RegExp('''(\\d+)°\\s*(\\d+)'\\s*(\\d+).(\\d+)"''');
final m = pattern.firstMatch(str);
if (m != null) {
final deg = double.tryParse(m[1]!)!;
final min = double.tryParse(m[2]!)!;
final sec = double.tryParse(m[3]!)!;
final frac = double.tryParse(m[4]!)!;
final d = deg + min / 60 + sec / (60 * 60) + frac / (60 * 60 * 100);
return d;
} else {
throw 'Invalid sexagesimal: $str';
}
}
/// Pads a number with a single zero, if it is less than 10
String zeroPad(num number) => (number < 10 ? '0' : '') + number.toString();
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of latlong2;
/// Circle-base GEO algorithms.
///
/// Circle uses by default the Vincenty-Algorithm for distance computations
class Circle {
// final Logger _logger = new Logger('latlong2.Circle');
final double radius;
final LatLng center;
final DistanceCalculator _calculator;
const Circle(this.center, this.radius,
{final DistanceCalculator calculator = const Vincenty()})
: _calculator = calculator;
/// Checks if a [point] is inside the given [Circle]
///
/// final Circle circle = new Circle(new LatLng(0.0,0.0), 111319.0);
/// final LatLng newPos = new LatLng(1.0,0.0);
///
/// expect(circle.isPointInside(newPos),isTrue);
///
/// final Circle circle2 = new Circle(new LatLng(0.0,0.0), 111318.0);
///
/// expect(circle2.isPointInside(newPos),isFalse);
///
bool isPointInside(final LatLng point) {
final distance = Distance(calculator: _calculator);
final dist = distance(center, point);
return dist <= radius;
}
//- private -----------------------------------------------------------------------------------
}
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of latlong2;
/// Calculates the distance between points.
///
/// Default algorithm is [distanceWithVincenty], default radius is [earthRadius]
///
/// final Distance distance = new Distance();
///
/// // km = 423
/// final int km = distance.as(LengthUnit.Kilometer,
/// new LatLng(52.518611,13.408056),new LatLng(51.519475,7.46694444));
///
/// // meter = 422592
/// final int meter = distance(new LatLng(52.518611,13.408056),new LatLng(51.519475,7.46694444));
///
class Distance implements DistanceCalculator {
// final Logger _logger = new Logger('latlong2.Distance');
final double _radius;
final _roundResult;
final DistanceCalculator _calculator;
const Distance(
{final bool roundResult = true,
final DistanceCalculator calculator = const Vincenty()})
: _radius = earthRadius,
_roundResult = roundResult,
_calculator = calculator;
/// Radius must be greater than 0.
Distance.withRadius(final double radius,
{final bool roundResult = true,
final DistanceCalculator calculator = const Vincenty()})
: _radius = radius,
_roundResult = roundResult,
_calculator = calculator {
if (radius <= 0) {
throw ArgumentError.value(
radius, 'radius', 'Radius must be greater than 0');
}
}
double get radius => _radius;
/// Returns either [Haversine] oder [Vincenty] calculator
///
/// final Distance distance = const DistanceHaversine();
/// final Circle circle = new Circle(base, 1000.0,calculator: distance.calculator);
///
DistanceCalculator get calculator => _calculator;
/// Shortcut for [distance]
double call(final LatLng p1, final LatLng p2) {
return distance(p1, p2);
}
/// Converts the distance to the given [LengthUnit]
///
/// final int km = distance.as(LengthUnit.Kilometer,
/// new LatLng(52.518611,13.408056),new LatLng(51.519475,7.46694444));
///
double as(final LengthUnit unit, final LatLng p1, final LatLng p2) {
final dist = _calculator.distance(p1, p2);
// If the distance is NaN or infinite, return 0.0
if(dist.isNaN || dist.isInfinite) {
return 0.0;
}
return _round(LengthUnit.Meter.to(unit, dist));
}
/// Computes the distance between two points.
///
/// The function uses the [DistanceAlgorithm] specified in the CTOR
@override
double distance(final LatLng p1, final LatLng p2) =>
_round(_calculator.distance(p1, p2));
/// Returns the great circle bearing (direction) in degrees to the next point ([p2])
///
/// Find out about the difference between rhumb line and
/// great circle bearing on [Wikipedia](http://en.wikipedia.org/wiki/Rhumb_line#General_and_mathematical_description).
///
/// final Distance distance = const Distance();
///
/// final LatLng p1 = new LatLng(0.0, 0.0);
/// final LatLng p2 = new LatLng(-90.0, 0.0);
///
/// expect(distance.direction(p1, p2), equals(180));
double bearing(final LatLng p1, final LatLng p2) {
final diffLongitude = p2.longitudeInRad - p1.longitudeInRad;
final y = math.sin(diffLongitude);
final x = math.cos(p1.latitudeInRad) * math.tan(p2.latitudeInRad) -
math.sin(p1.latitudeInRad) * math.cos(diffLongitude);
return radianToDeg(math.atan2(y, x));
}
/// Returns a destination point based on the given [distance] and [bearing]
///
/// Given a [from] (start) point, initial [bearing], and [distance],
/// this will calculate the destination point and
/// final bearing travelling along a (shortest distance) great circle arc.
///
/// final Distance distance = const Distance();
///
/// final num distanceInMeter = (earthRadius * math.PI / 4).round();
///
/// final p1 = new LatLng(0.0, 0.0);
/// final p2 = distance.offset(p1, distanceInMeter, 180);
///
/// Bearing: Left - 270°, right - 90°, up - 0°, down - 180°
@override
LatLng offset(
final LatLng from, final num distanceInMeter, final num bearing) =>
_calculator.offset(from, distanceInMeter.toDouble(), bearing.toDouble());
//- private -----------------------------------------------------------------------------------
double _round(final double value) =>
(_roundResult ? value.round().toDouble() : value);
}
/// Shortcut for
/// final Distance distance = const Distance(calculator: const Vincenty());
///
class DistanceVincenty extends Distance {
const DistanceVincenty({final bool roundResult = true})
: super(roundResult: roundResult, calculator: const Vincenty());
/// Radius must be greater than 0.
DistanceVincenty.withRadius(final double radius,
{final bool roundResult = true})
: super.withRadius(radius,
roundResult: roundResult, calculator: const Vincenty()) {
if (radius <= 0) {
throw ArgumentError.value(
radius, 'radius', 'Radius must be greater than 0');
}
}
}
/// Shortcut for
/// final Distance distance = const Distance(calculator: const Haversine());
///
class DistanceHaversine extends Distance {
const DistanceHaversine({final bool roundResult = true})
: super(roundResult: roundResult, calculator: const Haversine());
/// Radius must be greater than 0.
DistanceHaversine.withRadius(final double radius,
{final bool roundResult = true})
: super.withRadius(radius,
roundResult: roundResult, calculator: const Haversine()) {
if (radius <= 0) {
throw ArgumentError.value(
radius, 'radius', 'Radius must be greater than 0');
}
}
}
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of latlong2;
/// Coordinates in Degrees
///
/// final Location location = new Location(10.000002,12.00001);
///
class LatLng {
// final Logger _logger = new Logger('latlong2.LatLng');
final double latitude;
final double longitude;
const LatLng(this.latitude, this.longitude)
: assert(latitude >= -90 && latitude <= 90),
assert(longitude >= -180 && longitude <= 180);
double get latitudeInRad => degToRadian(latitude);
double get longitudeInRad => degToRadian(longitude);
LatLng.fromJson(Map<String, dynamic> json)
: latitude = json['coordinates'][1],
longitude = json['coordinates'][0];
Map<String, dynamic> toJson() => {
'coordinates': [longitude, latitude]
};
@override
String toString() =>
'LatLng(latitude:${NumberFormat("0.0#####").format(latitude)}, '
'longitude:${NumberFormat("0.0#####").format(longitude)})';
/// Converts sexagesimal string into a lat/long value
///
/// final LatLng p1 = new LatLng.fromSexagesimal('''51° 31' 10.11" N, 19° 22' 32.00" W''');
/// print("${p1.latitude}, ${p1.longitude}");
/// // Shows:
/// 51.519475, -19.37555556
///
factory LatLng.fromSexagesimal(final String str) {
double _latitude = 0.0;
double _longitude = 0.0;
// try format '''47° 09' 53.57" N, 8° 32' 09.04" E'''
var splits = str.split(',');
if (splits.length != 2) {
// try format '''N 47°08'52.57" E 8°32'09.04"'''
splits = str.split('E');
if (splits.length != 2) {
// try format '''N 47°08'52.57" W 8°32'09.04"'''
splits = str.split('W');
if (splits.length != 2) {
throw 'Unsupported sexagesimal format: $str';
}
}
}
_latitude = sexagesimal2decimal(splits[0]);
_longitude = sexagesimal2decimal(splits[1]);
if (str.contains('S')) {
_latitude = -_latitude;
}
if (str.contains('W')) {
_longitude = -_longitude;
}
return LatLng(_latitude, _longitude);
}
/// Converts lat/long values into sexagesimal
///
/// final LatLng p1 = new LatLng(51.519475, -19.37555556);
///
/// // Shows: 51° 31' 10.11" N, 19° 22' 32.00" W
/// print(p1..toSexagesimal());
///
String toSexagesimal() {
var latDirection = latitude >= 0 ? 'N' : 'S';
var lonDirection = longitude >= 0 ? 'E' : 'W';
return '${decimal2sexagesimal(latitude)} $latDirection, ${decimal2sexagesimal(longitude)} $lonDirection';
}
@override
int get hashCode => latitude.hashCode + longitude.hashCode;
@override
bool operator ==(final Object other) =>
other is LatLng &&
latitude == other.latitude &&
longitude == other.longitude;
LatLng round({final int decimals = 6}) => LatLng(
_round(latitude, decimals: decimals),
_round(longitude, decimals: decimals));
//- private -----------------------------------------------------------------------------------
/// No qualifier for top level functions in Dart. Had to copy this function
double _round(final double value, {final int decimals = 6}) =>
(value * math.pow(10, decimals)).round() / math.pow(10, decimals);
}
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of latlong2;
class LengthUnit {
static const LengthUnit Millimeter = LengthUnit(1000.0);
static const LengthUnit Centimeter = LengthUnit(100.0);
static const LengthUnit Meter = LengthUnit(1.0);
static const LengthUnit Kilometer = LengthUnit(0.001);
static const LengthUnit Mile = LengthUnit(0.0006213712);
final double scaleFactor;
const LengthUnit(this.scaleFactor);
double to(final LengthUnit unit, final double value) {
if (unit.scaleFactor == scaleFactor) {
return value;
}
// Convert to primary unit.
final primaryValue = value / scaleFactor;
// Convert to destination unit.
return primaryValue * unit.scaleFactor;
}
}
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of latlong2;
class Haversine implements DistanceCalculator {
// final Logger _logger = new Logger('latlong2.Haversine');
const Haversine();
/// Calculates distance with Haversine algorithm.
///
/// Accuracy can be out by 0.3%
/// More on [Wikipedia](https://en.wikipedia.org/wiki/Haversine_formula)
@override
double distance(final LatLng p1, final LatLng p2) {
final sinDLat = math.sin((p2.latitudeInRad - p1.latitudeInRad) / 2);
final sinDLng = math.sin((p2.longitudeInRad - p1.longitudeInRad) / 2);
// Sides
final a = sinDLat * sinDLat +
sinDLng *
sinDLng *
math.cos(p1.latitudeInRad) *
math.cos(p2.latitudeInRad);
final c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));
return equatorRadius * c;
}
/// Returns a destination point based on the given [distance] and [bearing]
///
/// Given a [from] (start) point, initial [bearing], and [distance],
/// this will calculate the destination point and
/// final bearing travelling along a (shortest distance) great circle arc.
///
/// final Haversine distance = const Haversine();
///
/// final num distanceInMeter = (earthRadius * math.PI / 4).round();
///
/// final p1 = new LatLng(0.0, 0.0);
/// final p2 = distance.offset(p1, distanceInMeter, 180);
///
@override
LatLng offset(
final LatLng from, final double distanceInMeter, final double bearing) {
if (bearing < -180 || bearing > 180) {
throw ArgumentError.value(
bearing, 'bearing', 'Angle must be between -180 and 180 degrees');
}
final h = degToRadian(bearing.toDouble());
final a = distanceInMeter / equatorRadius;
final lat2 = math.asin(math.sin(from.latitudeInRad) * math.cos(a) +
math.cos(from.latitudeInRad) * math.sin(a) * math.cos(h));
final lng2 = from.longitudeInRad +
math.atan2(math.sin(h) * math.sin(a) * math.cos(from.latitudeInRad),
math.cos(a) - math.sin(from.latitudeInRad) * math.sin(lat2));
return LatLng(radianToDeg(lat2), radianToDeg(lng2));
}
//- private -----------------------------------------------------------------------------------
}
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of latlong2;
class Vincenty implements DistanceCalculator {
// final Logger _logger = new Logger('latlong2.Vincenty');
const Vincenty();
/// Calculates distance with Vincenty algorithm.
///
/// Accuracy is about 0.5mm
/// More on [Wikipedia](https://en.wikipedia.org/wiki/Vincenty%27s_formulae)
@override
double distance(final LatLng p1, final LatLng p2) {
var a = equatorRadius,
b = polarRadius,
f = flattening; // WGS-84 ellipsoid params
var l = p2.longitudeInRad - p1.longitudeInRad;
var u1 = math.atan((1 - f) * math.tan(p1.latitudeInRad));
var u2 = math.atan((1 - f) * math.tan(p2.latitudeInRad));
var sinU1 = math.sin(u1), cosU1 = math.cos(u1);
var sinU2 = math.sin(u2), cosU2 = math.cos(u2);
double sinLambda,
cosLambda,
sinSigma,
cosSigma,
sigma,
sinAlpha,
cosSqAlpha,
cos2SigmaM;
double lambda = l, lambdaP;
var maxIterations = 200;
do {
sinLambda = math.sin(lambda);
cosLambda = math.cos(lambda);
sinSigma = math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) +
(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) *
(cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
if (sinSigma == 0) {
return 0.0; // co-incident points
}
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
sigma = math.atan2(sinSigma, cosSigma);
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
cosSqAlpha = 1 - sinAlpha * sinAlpha;
cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
if (cos2SigmaM.isNaN) {
cos2SigmaM = 0.0; // equatorial line: cosSqAlpha=0 (§6)
}
var C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
lambdaP = lambda;
lambda = l +
(1 - C) *
f *
sinAlpha *
(sigma +
C *
sinSigma *
(cos2SigmaM +
C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
} while ((lambda - lambdaP).abs() > 1e-12 && --maxIterations > 0);
if (maxIterations == 0) {
throw StateError('Distance calculation faild to converge!');
}
var uSq = cosSqAlpha * (a * a - b * b) / (b * b);
var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
var deltaSigma = B *
sinSigma *
(cos2SigmaM +
B /
4 *
(cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
B /
6 *
cos2SigmaM *
(-3 + 4 * sinSigma * sinSigma) *
(-3 + 4 * cos2SigmaM * cos2SigmaM)));
var dist = b * A * (sigma - deltaSigma);
return dist;
}
/// Vincenty inverse calculation
///
/// More on [Wikipedia](https://en.wikipedia.org/wiki/Vincenty%27s_formulae)
@override
LatLng offset(
final LatLng from, final double distanceInMeter, final double bearing) {
final equatorialRadius = equatorRadius;
final latitude = from.latitudeInRad;
final longitude = from.longitudeInRad;
final alpha1 = degToRadian(bearing);
final sinAlpha1 = math.sin(alpha1);
final cosAlpha1 = math.cos(alpha1);
final tanU1 = (1 - flattening) * math.tan(latitude);
final cosU1 = 1 / math.sqrt((1 + tanU1 * tanU1));
final sinU1 = tanU1 * cosU1;
final sigma1 = math.atan2(tanU1, cosAlpha1);
final sinAlpha = cosU1 * sinAlpha1;
final cosSqAlpha = 1 - sinAlpha * sinAlpha;
final dfUSq = cosSqAlpha *
(equatorialRadius * equatorialRadius - polarRadius * polarRadius) /
(polarRadius * polarRadius);
final a = 1 +
dfUSq / 16384 * (4096 + dfUSq * (-768 + dfUSq * (320 - 175 * dfUSq)));
final b = dfUSq / 1024 * (256 + dfUSq * (-128 + dfUSq * (74 - 47 * dfUSq)));
var sigma = distanceInMeter / (polarRadius * a);
var sigmaP = 2 * pi;
var sinSigma = 0.0;
var cosSigma = 0.0;
var cos2SigmaM = 0.0;
double deltaSigma;
var maxIterations = 200;
do {
cos2SigmaM = math.cos(2 * sigma1 + sigma);
sinSigma = math.sin(sigma);
cosSigma = math.cos(sigma);
deltaSigma = b *
sinSigma *
(cos2SigmaM +
b /
4 *
(cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) -
b /
6 *
cos2SigmaM *
(-3 + 4 * sinSigma * sinSigma) *
(-3 + 4 * cos2SigmaM * cos2SigmaM)));
sigmaP = sigma;
sigma = distanceInMeter / (polarRadius * a) + deltaSigma;
} while ((sigma - sigmaP).abs() > 1e-12 && --maxIterations > 0);
if (maxIterations == 0) {
throw StateError('offset calculation faild to converge!');
}
final tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
final lat2 = math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
(1 - flattening) * math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
final lambda = math.atan2(
sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
final c =
flattening / 16 * cosSqAlpha * (4 + flattening * (4 - 3 * cosSqAlpha));
final l = lambda -
(1 - c) *
flattening *
sinAlpha *
(sigma +
c *
sinSigma *
(cos2SigmaM +
c * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
var lon2 = longitude + l;
// print("LA ${radianToDeg(lat2)}, LO ${radianToDeg(lon2)}");
if (lon2 > pi) {
lon2 = lon2 - 2 * pi;
}
if (lon2 < -1 * pi) {
lon2 = lon2 + 2 * pi;
}
return LatLng(radianToDeg(lat2), radianToDeg(lon2));
}
//- private -----------------------------------------------------------------------------------
}
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of latlong2;
abstract class DistanceCalculator {
double distance(final LatLng p1, final LatLng p2);
LatLng offset(
final LatLng from, final double distanceInMeter, final double bearing);
}
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// Splines are most commonly used to draw a curve
/// line when a set of given points exists, which needs to be joined smoothly.
///
/// More about the [algorithm](http://www.dxstudio.com/guide_content.aspx?id=70a2b2cf-193e-4019-859c-28210b1da81f)
/// and [here](http://www.mvps.org/directx/articles/catmull/).
///
/// Java way: [A Catmull Rom Spline (curve) Implementation in Java](http://hawkesy.blogspot.co.at/2010/05/catmull-rom-spline-curve-implementation.html)
///
library spline;
//import 'package:logging/logging.dart';
part 'spline/CatmullRomSpline.dart';
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of spline;
class Point2D {
final double x;
final double y;
Point2D(this.x, this.y);
}
abstract class CatmullRom<R> {
const CatmullRom();
R position(final double distance);
R percentage(final num percent) => position(percent / 100);
}
class CatmullRomSpline extends CatmullRom<double> {
// final Logger _logger = new Logger('spline.CatmullRomSpline');
final double _p0, _p1, _p2, _p3;
const CatmullRomSpline(this._p0, this._p1, this._p2, this._p3);
const CatmullRomSpline.noEndpoints(final double p1, final double p2)
: _p0 = p1,
_p1 = p1,
_p2 = p2,
_p3 = p2;
@override
double position(final double distance) {
if (distance < 0 || distance > 1) {
throw ArgumentError.value(
distance, 'distance', 'Distance must be beteen 0 and 1.');
}
return 0.5 *
((2 * _p1) +
(_p2 - _p0) * distance +
(2 * _p0 - 5 * _p1 + 4 * _p2 - _p3) * distance * distance +
(3 * _p1 - _p0 - 3 * _p2 + _p3) * distance * distance * distance);
}
}
class CatmullRomSpline2D<T extends num> extends CatmullRom<Point2D> {
final Point2D _p0;
final Point2D _p1;
final Point2D _p2;
final Point2D _p3;
CatmullRomSpline2D(this._p0, this._p1, this._p2, this._p3);
CatmullRomSpline2D.noEndpoints(final Point2D p0, final Point2D p1)
: _p0 = p0,
_p1 = p0,
_p2 = p1,
_p3 = p1;
@override
Point2D position(final double distance) {
if (distance < 0 || distance > 1) {
throw ArgumentError.value(
distance, 'distance', 'Distance must be beteen 0 and 1.');
}
return Point2D(
CatmullRomSpline(_p0.x, _p1.x, _p2.x, _p3.x).position(distance),
CatmullRomSpline(_p0.y, _p1.y, _p2.y, _p3.y).position(distance));
}
}
This diff is collapsed. Click to expand it.
name: latlong2
description: Lightweight library for common latitude and longitude calculation
homepage: https://github.com/jifalops/dart-latlong
version: 0.9.0
environment:
sdk: ">=3.0.0 <4.0.0"
dependencies:
intl: '>=0.15.0 <1.0.0'
dev_dependencies:
test: any
lints: any
build_runner: any
build_test: any
build_web_compilers: any
//@TestOn("content-shell")
import 'package:test/test.dart';
import 'package:latlong2/latlong.dart';
// import 'package:logging/logging.dart';
// Browser
// import "package:console_log_handler/console_log_handler.dart";
// Commandline
// import "package:console_log_handler/print_log_handler.dart";
void main() {
// final Logger _logger = new Logger("test.Circle");
// configLogging();
final base = LatLng(0.0, 0.0);
final distance = const Distance();
final circle = Circle(base, 1000.0);
final Distance distanceHaversine = const DistanceHaversine();
final circleHaversine = Circle(base, 1000.0, calculator: const Haversine());
group('Circle with Vincenty', () {
setUp(() {});
test(
'> isInside - distance from 0.0,0.0 to 1.0,0.0 is 110574 meter (based on Vincenty)',
() {
final circle = Circle(LatLng(0.0, 0.0), 110574.0);
final newPos = LatLng(1.0, 0.0);
// final double dist = new Distance().distance(circle.center,newPos);
// print(dist);
expect(circle.isPointInside(newPos), isTrue);
final circle2 = Circle(LatLng(0.0, 0.0), 110573.0);
expect(circle2.isPointInside(newPos), isFalse);
}); // end of 'isInside - ' test
test('> isInside, bearing 0', () {
final num bearing = 0;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circle.isPointInside(distance.offset(base, dist, bearing)), isTrue);
});
expect(
circle.isPointInside(distance.offset(base, 1001, bearing)), isFalse);
}); // end of 'isInside, bearing 0' test
test('> isInside, bearing 90', () {
final num bearing = 90;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circle.isPointInside(distance.offset(base, dist, bearing)), isTrue);
});
expect(
circle.isPointInside(distance.offset(base, 1001, bearing)), isFalse);
}); // end of 'isInside, bearing 0' test
test('> isInside, bearing -90', () {
final num bearing = -90;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circle.isPointInside(distance.offset(base, dist, bearing)), isTrue);
});
expect(
circle.isPointInside(distance.offset(base, 1001, bearing)), isFalse);
}); // end of 'isInside, bearing 0' test
test('> isInside, bearing 180', () {
final num bearing = 180;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circle.isPointInside(distance.offset(base, dist, bearing)), isTrue);
});
expect(
circle.isPointInside(distance.offset(base, 1001, bearing)), isFalse);
}); // end of 'isInside, bearing 0' test
test('> isInside, bearing -180', () {
final num bearing = -180;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circle.isPointInside(distance.offset(base, dist, bearing)), isTrue);
});
expect(
circle.isPointInside(distance.offset(base, 1001, bearing)), isFalse);
}); // end of 'isInside, bearing 0' test
});
// End of 'Circle with Haversine' group
group('Circle with Haversine', () {
test('> isInside, bearing 0', () {
final num bearing = 0;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, dist, bearing)),
isTrue);
});
<num>[1001, 1002, 1003, 1004, 1005, 1006, 1007].forEach((final num dist) {
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, dist, bearing)),
isFalse);
});
}); // end of 'isInside, bearing 0' test
test('> isInside, bearing 90', () {
final num bearing = 90;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, dist, bearing)),
isTrue);
});
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, 1001, bearing)),
isFalse);
}); // end of 'isInside, bearing 0' test
test('> isInside, bearing -90', () {
final num bearing = -90;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, dist, bearing)),
isTrue);
});
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, 1001, bearing)),
isFalse);
}); // end of 'isInside, bearing 0' test
test('> isInside, bearing 180', () {
final num bearing = 180;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, dist, bearing)),
isTrue);
});
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, 1001, bearing)),
isFalse);
}); // end of 'isInside, bearing 0' test
test('> isInside, bearing -180', () {
final num bearing = -180;
<num>[100, 500, 999, 1000].forEach((final num dist) {
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, dist, bearing)),
isTrue);
});
expect(
circleHaversine
.isPointInside(distanceHaversine.offset(base, 1001, bearing)),
isFalse);
}); // end of 'isInside, bearing 0' test
}); // End of 'Circle with Vincenty' group
}
// - Helper --------------------------------------------------------------------------------------
//@TestOn("content-shell")
import 'package:test/test.dart';
import 'package:latlong2/latlong.dart';
// import 'package:logging/logging.dart';
// Browser
// import "package:console_log_handler/console_log_handler.dart";
// Commandline
// import "package:console_log_handler/print_log_handler.dart";
void main() {
// final Logger _logger = new Logger("test.Distance");
// configLogging();
group('Distance', () {
setUp(() {});
test('> Radius', () {
expect((Distance()).radius, earthRadius);
expect((Distance.withRadius(100.0)).radius, 100.0);
}); // end of 'Radius' test
test('> Distance to the same point is 0', () {
final compute = Distance();
final p = LatLng(0.0, 0.0);
expect(compute.distance(p, p), equals(0));
}); // end of 'Simple distance' test
test('> Distance between 0 and 90.0 is around 10.000km', () {
final distance = Distance();
final p1 = LatLng(0.0, 0.0);
final p2 = LatLng(90.0, 0.0);
// no rounding
expect(distance(p1, p2) ~/ 1000, equals(10001));
expect(
LengthUnit.Meter.to(LengthUnit.Kilometer, distance(p1, p2)).round(),
equals(10002));
// rounds to 10002
expect(distance.as(LengthUnit.Kilometer, p1, p2), equals(10002));
expect(distance.as(LengthUnit.Meter, p1, p2), equals(10001966));
}); // end of 'Distance between 0 and 90.0' test
test('> Distance between 0 and 90.0 is 10001.96572931165 km ', () {
final distance = Distance(roundResult: false);
final p1 = LatLng(0.0, 0.0);
final p2 = LatLng(90.0, 0.0);
expect(
distance.as(LengthUnit.Kilometer, p1, p2), equals(10001.96572931165));
}); // end of 'Round' test
test('> distance between 0,-180 and 0,180 is 0', () {
final distance = Distance();
final p1 = LatLng(0.0, -180.0);
final p2 = LatLng(0.0, 180.0);
expect(distance(p1, p2), 0);
}); // end of 'distance between 0,-180 and 0,180 is 0' test
group('Vincenty', () {
test('> Test 1', () {
final distance = Distance();
expect(
distance(
LatLng(52.518611, 13.408056), LatLng(51.519475, 7.46694444)),
422592);
expect(
distance.as(LengthUnit.Kilometer, LatLng(52.518611, 13.408056),
LatLng(51.519475, 7.46694444)),
423);
});
}); // End of 'Vincenty' group
group('Haversine - not so accurate', () {
test('> Test 1', () {
final distance = Distance(calculator: const Haversine());
expect(
distance(
LatLng(52.518611, 13.408056), LatLng(51.519475, 7.46694444)),
421786.0);
});
}); // End of 'Haversine' group
});
// End of 'Distance' group
group('Bearing', () {
test('bearing to the same point is 0 degree', () {
final distance = const Distance();
final p = LatLng(0.0, 0.0);
expect(distance.bearing(p, p), equals(0));
});
test('bearing between 0,0 and 90,0 is 0 degree', () {
final distance = const Distance();
final p1 = LatLng(0.0, 0.0);
final p2 = LatLng(90.0, 0.0);
expect(distance.bearing(p1, p2), equals(0));
});
test('bearing between 0,0 and -90,0 is 180 degree', () {
final distance = const Distance();
final p1 = LatLng(0.0, 0.0);
final p2 = LatLng(-90.0, 0.0);
expect(distance.bearing(p1, p2), equals(180));
});
test('bearing between 0,-90 and 0,90 is -90 degree', () {
final distance = const Distance();
final p1 = LatLng(0.0, -90.0);
final p2 = LatLng(0.0, 90.0);
expect(distance.bearing(p1, p2), equals(90));
});
test('bearing between 0,-180 and 0,180 is -90 degree', () {
final distance = const Distance();
final p1 = LatLng(0.0, -180.0);
final p2 = LatLng(0.0, 180.0);
expect(distance.bearing(p1, p2), equals(-90));
expect(normalizeBearing(distance.bearing(p1, p2)), equals(270));
});
}); // End of 'Direction' group
group('Offset', () {
test('offset from 0,0 with bearing 0 and distance 10018.754 km is 90,180',
() {
final distance = const Distance();
final num distanceInMeter = (earthRadius * pi / 2).round();
//print("Dist $distanceInMeter");
final p1 = LatLng(0.0, 0.0);
final p2 = distance.offset(p1, distanceInMeter.round(), 0);
//print(p2);
//print("${decimal2sexagesimal(p2.latitude)} / ${decimal2sexagesimal(p2.longitude)}");
expect(p2.latitude.round(), equals(90));
expect(p2.longitude.round(), equals(180));
});
test('offset from 0,0 with bearing 180 and distance ~ 5.000 km is -45,0',
() {
final distance = const Distance();
final num distanceInMeter = (earthRadius * pi / 4).round();
final p1 = LatLng(0.0, 0.0);
final p2 = distance.offset(p1, distanceInMeter, 180);
// print(p2.round());
// print(p2.toSexagesimal());
expect(p2.latitude.round(), equals(-45));
expect(p2.longitude.round(), equals(0));
});
test('offset from 0,0 with bearing 180 and distance ~ 10.000 km is -90,180',
() {
final distance = const Distance();
final num distanceInMeter = (earthRadius * pi / 2).round();
final p1 = LatLng(0.0, 0.0);
final p2 = distance.offset(p1, distanceInMeter, 180);
expect(p2.latitude.round(), equals(-90));
expect(p2.longitude.round(), equals(180)); // 0 Vincenty
});
test('offset from 0,0 with bearing 90 and distance ~ 5.000 km is 0,45', () {
final distance = const Distance();
final num distanceInMeter = (earthRadius * pi / 4).round();
final p1 = LatLng(0.0, 0.0);
final p2 = distance.offset(p1, distanceInMeter, 90);
expect(p2.latitude.round(), equals(0));
expect(p2.longitude.round(), equals(45));
});
}); // End of 'Offset' group
}
// - Helper --------------------------------------------------------------------------------------
/*
* Copyright (c) 2016, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'package:test/test.dart';
import 'package:latlong2/latlong.dart';
// import 'package:logging/logging.dart';
// Browser
// import "package:console_log_handler/console_log_handler.dart";
// Commandline
// import "package:console_log_handler/print_log_handler.dart";
void main() {
// final Logger _logger = new Logger("test.LatLng");
// configLogging();
group('A group of tests', () {
setUp(() {});
test('> Range', () {
expect(() => LatLng(-80.0, 0.0), returnsNormally);
expect(() => LatLng(-100.0, 0.0), throwsAssertionError);
expect(() => LatLng(80.0, 0.0), returnsNormally);
expect(() => LatLng(100.0, 0.0), throwsAssertionError);
expect(() => LatLng(0.0, -170.0), returnsNormally);
expect(() => LatLng(0.0, -190.0), throwsAssertionError);
expect(() => LatLng(0.0, 170.0), returnsNormally);
expect(() => LatLng(0.0, 190.0), throwsAssertionError);
}); // end of 'Range' test
test('> Rad', () {
expect((LatLng(-80.0, 0.0)).latitudeInRad, -1.3962634015954636);
expect((LatLng(90.0, 0.0)).latitudeInRad, 1.5707963267948966);
expect((LatLng(0.0, 80.0)).longitudeInRad, 1.3962634015954636);
expect((LatLng(0.0, 90.0)).longitudeInRad, 1.5707963267948966);
}); // end of 'Rad' test
test('> toString', () {
expect((LatLng(-80.0, 0.0)).toString(),
'LatLng(latitude:-80.0, longitude:0.0)');
expect((LatLng(-80.123456, 0.0)).toString(),
'LatLng(latitude:-80.123456, longitude:0.0)');
}); // end of 'toString' test
test('> toJson', () {
expect((LatLng(-80.0, 0.0)).toJson(), {
'coordinates': [0.0, -80.0]
});
expect((LatLng(0.0, 80.0)).toJson(), {
'coordinates': [80.0, 0.0]
});
});
test('> fromJson', () {
expect(
LatLng.fromJson({
'coordinates': [0.0, -80.0]
}),
LatLng(-80.0, 0.0));
expect(
LatLng.fromJson({
'coordinates': [80.0, 0.0]
}),
LatLng(0.0, 80.0));
});
test('> equal', () {
expect(LatLng(-80.0, 0.0), LatLng(-80.0, 0.0));
expect(LatLng(-80.0, 0.0), isNot(LatLng(-80.1, 0.0)));
expect(LatLng(-80.0, 0.0), isNot(LatLng(0.0, 80.0)));
}); // end of 'equal' test
});
}
final Matcher throwsAssertionError = throwsA(isA<AssertionError>());
//@TestOn("content-shell")
import 'package:test/test.dart';
import 'package:latlong2/latlong.dart';
// import 'package:logging/logging.dart';
// Browser
// import "package:console_log_handler/console_log_handler.dart";
// Commandline
// import "package:console_log_handler/print_log_handler.dart";
void main() async {
// final Logger _logger = new Logger("test.LengthUnit");
// configLogging();
//await saveDefaultCredentials();
group('LengthUnit', () {
setUp(() {});
test('> Millimeter', () {
expect(LengthUnit.Millimeter.to(LengthUnit.Millimeter, 1.0), 1.0);
expect(LengthUnit.Millimeter.to(LengthUnit.Centimeter, 1.0), 0.1);
expect(LengthUnit.Millimeter.to(LengthUnit.Meter, 1000.0), 1.0);
expect(LengthUnit.Millimeter.to(LengthUnit.Kilometer, 1000000.0), 1);
}); // end of 'Millimeter' test
test('> Centimeter', () {
expect(LengthUnit.Centimeter.to(LengthUnit.Centimeter, 1.0), 1.0);
expect(LengthUnit.Centimeter.to(LengthUnit.Millimeter, 1.0), 10.0);
}); // end of 'Centimeter' test
test('> Meter', () {
expect(LengthUnit.Meter.to(LengthUnit.Meter, 100.0), 100.0);
expect(LengthUnit.Meter.to(LengthUnit.Kilometer, 1.0), 0.001);
}); // end of 'Meter' test
test('> Kilometer', () {
expect(LengthUnit.Kilometer.to(LengthUnit.Kilometer, 1.0), 1.0);
expect(LengthUnit.Kilometer.to(LengthUnit.Meter, 1.0), 1000.0);
}); // end of 'Kilometer' test
test('> Mike', () {
expect((LengthUnit.Mile.to(LengthUnit.Meter, 1.0) * 100).round() / 100,
1609.34);
}); // end of 'Mike' test
});
// End of 'LengthUnit' group
}
// - Helper --------------------------------------------------------------------------------------
//@TestOn("content-shell")
import 'package:test/test.dart';
import 'package:latlong2/latlong.dart';
// import 'package:logging/logging.dart';
// Browser
// import "package:console_log_handler/console_log_handler.dart";
// Commandline
// import "package:console_log_handler/print_log_handler.dart";
final Map<String, LatLng> cities = <String, LatLng>{
'berlin': LatLng(52.518611, 13.408056),
'moscow': LatLng(55.751667, 37.617778),
};
final List<LatLng> route = <LatLng>[
LatLng(51.513357512, 7.45574331),
LatLng(51.515400598, 7.45518541),
LatLng(51.516241842, 7.456494328),
LatLng(51.516722545, 7.459863183),
LatLng(51.517443592, 7.463232037),
LatLng(51.5177507, 7.464755532),
LatLng(51.517657233, 7.466622349),
LatLng(51.51722995, 7.468317505),
LatLng(51.516816015, 7.47011995),
LatLng(51.516308606, 7.471793648),
LatLng(51.515974782, 7.472437378),
LatLng(51.515413951, 7.472845074),
LatLng(51.514559338, 7.472909447),
LatLng(51.512195717, 7.472651955),
LatLng(51.511127373, 7.47140741),
LatLng(51.51029939, 7.469948288),
LatLng(51.509831973, 7.468446251),
LatLng(51.509978876, 7.462481019),
LatLng(51.510913701, 7.460678574),
LatLng(51.511594777, 7.459434029),
LatLng(51.512396029, 7.457695958),
LatLng(51.513317451, 7.45574331),
];
final List<LatLng> westendorf = <LatLng>[
LatLng(47.43074295001961, 12.21235112213462),
LatLng(47.43089093351458, 12.21272597555608),
LatLng(47.43112096728846, 12.21318739290575),
LatLng(47.43136362193013, 12.21357041557469),
LatLng(47.43151718768905, 12.21381341692645),
LatLng(47.43165029999054, 12.2140511609222),
LatLng(47.43197227207169, 12.21443856698021),
];
final List<LatLng> zigzag = <LatLng>[
LatLng(47.43082546234226, 12.21255804885847),
LatLng(47.43103958915331, 12.21268605330973),
LatLng(47.43105710900187, 12.21307899558343),
LatLng(47.43122940724644, 12.21334560213179),
LatLng(47.43140402736853, 12.21345312442578),
LatLng(47.43145463473182, 12.21370919972242),
LatLng(47.43152498372309, 12.21383217398376),
LatLng(47.43154236046533, 12.213861433609),
LatLng(47.43156491014229, 12.21389982585238),
LatLng(47.43170715787343, 12.21411329481371),
LatLng(47.4316056796912, 12.21427241091704),
LatLng(47.43148429441857, 12.21439779676563),
LatLng(47.43144240029867, 12.21446788249065),
LatLng(47.43150069195054, 12.21456420272734),
LatLng(47.4315919174373, 12.21469743884608),
LatLng(47.43163947608171, 12.21477097582562),
LatLng(47.43171300672132, 12.21474044606232),
LatLng(47.43178565483553, 12.21464852517297),
LatLng(47.43186412401507, 12.21455971070946),
LatLng(47.43196361890569, 12.21443596175264)
];
void main() {
// final Logger _logger = new Logger("test.Utils");
// configLogging();
group('Equalize path', () {
test(
'> The total size of a path with 1000m lengt devided by 10sections must have the same'
'length as the base path', () {
final distance = Distance();
final startPos = LatLng(0.0, 0.0);
final endPos = distance.offset(startPos, 1000, 0);
expect(distance(startPos, endPos), 1000);
final path = Path.from(<LatLng>[startPos, endPos]);
expect(path.distance, 1000);
final steps = path.equalize(100, smoothPath: false);
// _exportForGoogleEarth(steps);
expect(steps.distance, 1000);
expect(steps.coordinates.length, 11);
}); // end of '10 intermediate steps in 1000m should have the same length' test
test(
'> 10 smoothd out steps in total have approximatly!!! the same lenght '
'as the base path', () {
final distance = Distance();
final startPos = LatLng(0.0, 0.0);
final endPos = distance.offset(startPos, 1000, 0);
expect(distance(startPos, endPos), 1000);
final path = Path.from(<LatLng>[startPos, endPos]);
expect(path.distance, 1000);
final steps = path.equalize(100, smoothPath: false);
expect(steps.distance, inInclusiveRange(999, 1001));
expect(steps.coordinates.length, 11);
//_exportForGoogleEarth(steps);
for (var index = 0; index < steps.nrOfCoordinates - 1; index++) {
// 46?????
expect(distance(steps[index], steps[index + 1]),
inInclusiveRange(46, 112));
}
}); // end of '10 intermediate steps in 1000m should have the same length' test
test('> Path with 3 sections', () {
final distance = Distance();
final startPos = LatLng(0.0, 0.0);
final pos1 = distance.offset(startPos, 50, 0);
final pos2 = distance.offset(pos1, 15, 0);
final pos3 = distance.offset(pos2, 5, 0);
expect(distance(startPos, pos3), 70);
final path = Path.from(<LatLng>[startPos, pos1, pos2, pos3]);
expect(path.distance, 70);
final steps = path.equalize(30, smoothPath: false);
//_exportForGoogleEarth(steps);
expect(steps.nrOfCoordinates, 4);
}); // end of 'Path with 3 sections' test
test(
'> Reality Test - Westendorf, short, should 210m (same as Google Earth)',
() {
final path = Path.from(westendorf);
expect(path.distance, 210);
// first point to last point!
final distance = Distance();
expect(distance(westendorf.first, westendorf.last), 209);
final steps = path.equalize(5);
expect(steps.nrOfCoordinates, 44);
_exportForGoogleEarth(steps, show: false);
}); // end of 'Reality Test - Westendorf, short' test
test(
'> ZigZag, according to Google-Earth - 282m,'
'first to last point 190m (acc. movable-type.co.uk (Haversine)', () {
final path = Path.from(zigzag);
expect(path.distance, 282);
// first point to last point!
final distance = Distance();
expect(distance(zigzag.first, zigzag.last), 190);
final steps = path.equalize(8, smoothPath: true);
// 282 / 8 = 35,25 + first + last
expect(steps.nrOfCoordinates, 36);
expect(steps.coordinates.length, inInclusiveRange(36, 37));
_exportForGoogleEarth(steps, show: false);
// Distance check makes no sense - path is shorter than the original one!
// double sumDist = 0.0;
// for(int index = 0;index < steps.nrOfCoordinates - 1;index++) {
// sumDist += distance(steps[index],steps[index + 1]);
// }
}); // end of 'ZigZag' test
}); // End of 'Intermediate steps' group
group('PathLength', () {
test('> Distance of empty path should be 0', () {
final path = Path();
expect(path.distance, 0);
}); // end of 'Distance of empty path should be 0' test
test('> Path length should be 3377m', () {
final path = Path.from(route);
expect(path.distance, 3377);
}); // end of 'Path length should be 3377m' test
test('> Path lenght should be 3.377km', () {
final path = Path.from(route);
expect(
round(LengthUnit.Meter.to(LengthUnit.Kilometer, path.distance),
decimals: 3),
3.377);
}); // end of 'Path length should be 3.377km' test
}); // End of 'PathLength' group
group('Center', () {
test(
'> Center between Berlin and Moscow should be near Minsk '
'(54.743683,25.033239)', () {
final path = Path.from([cities['berlin']!, cities['moscow']!]);
expect(path.center.latitude, 54.743683);
expect(path.center.longitude, 25.033239);
}); // end of 'Center' test
}); // End of 'Center' group
group('Utils', () {
setUp(() {});
test('> Round', () {
expect(round(123.1), 123.1);
expect(round(123.123456), 123.123456);
expect(round(123.1234567), 123.123457);
expect(round(123.1234565), 123.123457);
expect(round(123.1234564), 123.123456);
expect(round(123.1234564, decimals: 0), 123);
expect(round(123.1234564, decimals: -1), 120);
expect(round(123.1234564, decimals: -3), 0);
expect(round(523.1234564, decimals: -3), 1000);
expect(round(423.1234564, decimals: -3), 0);
}); // end of 'Round' test
});
// End of 'Utils' group
}
// - Helper --------------------------------------------------------------------------------------
/// Print CSV-date on the cmdline
void _exportForGoogleEarth(final Path steps, {final bool show = true}) {
if (show) {
final distance = Distance();
print('latitude,longitude,distance');
for (var index = 0; index < steps.nrOfCoordinates - 1; index++) {
print(
'${steps[index].latitude}, ${steps[index].longitude}, ${distance(steps[index], steps[index + 1])}');
}
print('${steps.last.latitude}, ${steps.last.longitude}, 0');
}
}
//@TestOn("content-shell")
import 'package:test/test.dart';
import 'package:latlong2/latlong.dart';
// import 'package:logging/logging.dart';
// Browser
// import "package:console_log_handler/console_log_handler.dart";
// Commandline
// import "package:console_log_handler/print_log_handler.dart";
void main() async {
// final Logger _logger = new Logger("test.Sexagesimal");
// configLogging();
//await saveDefaultCredentials();
group('Sexagesimal', () {
setUp(() {});
test('> decimal2sexagesimal', () {
final sexa1 = decimal2sexagesimal(51.519475);
final sexa2 = decimal2sexagesimal(-19.392222222222223);
final sexa3 = decimal2sexagesimal(50.0);
expect(sexa1, '51° 31\' 10.11"');
expect(sexa2, '19° 23\' 32.00"');
expect(sexa3, '50° 00\' 00.00"');
final p1 = LatLng(51.519475, -19.392222222222223);
expect(p1.toSexagesimal(), '51° 31\' 10.11" N, 19° 23\' 32.00" W');
}); // end of 'decimal2sexagesimal' test
test('> sexagesimal2decimal', () {
// the code in the function documentation
final dec1 = sexagesimal2decimal('51° 31\' 10.11"');
expect(dec1, 51.519475);
final dec2 = sexagesimal2decimal('19° 23\' 32.00"');
expect(dec2, 19.392222222222223);
// round value
expect(50.0, sexagesimal2decimal('50° 00\' 00.00"'));
}); // end of 'sexagesimal2decimal' test
test('> sexagesimal2decimal2sexagesimal', () {
final sexa = '51° 31\' 10.11" N, 19° 23\' 32.00" W';
expect(LatLng.fromSexagesimal(sexa).toSexagesimal(), sexa);
}); // end of 'sexagesimal2decimal2sexagesimal' test
});
// End of 'Sexagesimal' group
}
// - Helper --------------------------------------------------------------------------------------
import 'package:test/test.dart';
import 'package:latlong2/spline.dart';
// import 'package:logging/logging.dart';
// Browser
// import "package:console_log_handler/console_log_handler.dart";
// Commandline
// import "package:console_log_handler/print_log_handler.dart";
void main() async {
// final Logger _logger = new Logger("test.CatmullRom");
// configLogging();
group('CatmullRom 1D', () {
setUp(() {});
test('> one dimension', () {
final spline = CatmullRomSpline(1, 2, 2, 1);
expect(spline.position(0.25), 2.09375);
expect(spline.position(0.5), 2.125);
expect(spline.position(0.75), 2.09375);
}); // end of 'one dimension' test
test('> no endpoints', () {
final spline = CatmullRomSpline.noEndpoints(1, 2);
expect(spline.position(0.25), 1.203125);
expect(spline.position(0.5), 1.5);
expect(spline.percentage(50), 1.5);
expect(spline.position(0.75), 1.796875);
}); // end of 'no endpoints' test
});
// End of 'CatmullRom 1D' group
group('CatmullRom 2D', () {
test('> Simple values', () {
final spline = CatmullRomSpline2D(
Point2D(1, 1), Point2D(2, 2), Point2D(2, 2), Point2D(1, 1));
expect(spline.position(0.25).x, 2.09375);
expect(spline.position(0.25).y, 2.09375);
expect(spline.position(0.5).x, 2.125);
expect(spline.position(0.5).y, 2.125);
expect(spline.percentage(50).x, 2.125);
expect(spline.percentage(50).y, 2.125);
expect(spline.position(0.75).x, 2.09375);
expect(spline.position(0.75).y, 2.09375);
});
test('> no Endpoints', () {
final spline =
CatmullRomSpline2D.noEndpoints(Point2D(1, 1), Point2D(2, 2));
expect(spline.position(0.25).x, 1.203125);
expect(spline.position(0.25).y, 1.203125);
}); // end of 'no Endpoints' test
test('> Exception', () {
final spline =
CatmullRomSpline2D.noEndpoints(Point2D(1, 1), Point2D(2, 2));
expect(() => spline.position(3.0).x, throwsArgumentError);
}); // end of 'Exception' test
}); // End of 'CatmullRom 2D' group
}
// - Helper --------------------------------------------------------------------------------------
import 'package:grinder/grinder.dart';
main(final List<String> args) => grind(args);
@DefaultTask()
@Depends(test)
build() {
}
@Task()
@Depends(analyze, testUnit)
test() {
}
@Task()
testUnit() {
new TestRunner().testAsync(files: "test/unit");
// All tests with @TestOn("content-shell") in header
// new TestRunner().test(files: "test/unit",platformSelector: "content-shell");
}
@Task()
analyze() {
final List<String> libs = [
"lib/latlong.dart"
];
libs.forEach((final String lib) => Analyzer.analyze(lib));
Analyzer.analyze("test");
}
@Task()
clean() => defaultClean();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment