diff --git a/.github/wiki/examples/basic-usage.md b/.github/wiki/examples/basic-usage.md index e8430d8..7581cad 100644 --- a/.github/wiki/examples/basic-usage.md +++ b/.github/wiki/examples/basic-usage.md @@ -22,24 +22,46 @@ void main() { final dir = compassDir(bearing); final km = distanceKm(lat, lng, kaabaLat, kaabaLng); - print('${city.padRight(18)}${bearing.toStringAsFixed(1).padLeft(6)}° ${dir.padRight(9)} ${km.toStringAsFixed(0)} km'); + print( + '${city.padRight(18)}' + '${bearing.toStringAsFixed(1).padLeft(6)}° ' + '${dir.padRight(9)} ' + '${km.toStringAsFixed(0)} km', + ); } } ``` +Expected output: + +``` +City Bearing Direction Distance +────────────────────────────────────────────────────── +New York 58.5° NE 9634 km +London 49.3° NE 5148 km +Istanbul 41.5° NE 2517 km +Jakarta 295.1° NW 7665 km +Cape Town 22.3° NE 8832 km +``` + ## Great-Circle Path Waypoints ```dart import 'package:qibla/qibla.dart'; void main() { - // 8 waypoints from London to Makkah - final path = greatCirclePath(51.5074, -0.1278, kaabaLat, kaabaLng, 8); + // 8 segments (9 waypoints) from London to Makkah + final path = qiblaGreatCircle(51.5074, -0.1278, steps: 8); print('Waypoints from London to Makkah:'); for (int i = 0; i < path.length; i++) { - final p = path[i]; - print(' ${i + 1}. ${p.latitude.toStringAsFixed(4)}°, ${p.longitude.toStringAsFixed(4)}°'); + print( + ' ${i + 1}. ' + '${path[i][0].toStringAsFixed(4)}°, ' + '${path[i][1].toStringAsFixed(4)}°', + ); } } ``` + +Each element of the returned list is a `[latitude, longitude]` pair in decimal degrees. The first point is the observer location; the last is the Ka'bah. diff --git a/README.md b/README.md index cd8fee1..f9a50ff 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![pub package](https://img.shields.io/pub/v/qibla.svg)](https://pub.dev/packages/qibla) [![CI](https://github.com/acamarata/qibla-dart/actions/workflows/ci.yml/badge.svg)](https://github.com/acamarata/qibla-dart/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![wiki](https://img.shields.io/badge/docs-wiki-blue.svg)](https://github.com/acamarata/qibla-dart/wiki) Qibla direction, great-circle path, and haversine distance for Dart and Flutter. Pure math, zero dependencies. @@ -20,7 +21,7 @@ import 'package:qibla/qibla.dart'; // Bearing from New York to the Ka'bah final bearing = qiblaAngle(40.7128, -74.006); -print(bearing); // ~58.48 +print(bearing); // ~58.48 print(compassDir(bearing)); // NE // Distance in kilometers @@ -30,60 +31,30 @@ print(km); // ~9634 ## API -### `qiblaAngle(lat, lng)` +| Function | Returns | Description | +| --- | --- | --- | +| `qiblaAngle(lat, lng)` | `double` | Bearing to Ka'bah, degrees clockwise from north | +| `compassDir(bearing)` | `String` | Eight-point abbreviation: N, NE, E, SE, S, SW, W, NW | +| `compassName(bearing)` | `String` | Full name: North, Northeast, ... | +| `qiblaGreatCircle(lat, lng, [steps])` | `List>` | Great-circle waypoints to the Ka'bah | +| `distanceKm(lat1, lng1, lat2, lng2)` | `double` | Haversine distance in km | -Computes the initial bearing (forward azimuth) from the given coordinates to the Ka'bah. +Constants: `kaabaLat` (21.422511), `kaabaLng` (39.82615), `earthRadiusKm` (6371). -| Parameter | Type | Description | -| ------------ | -------- | ----------------------------------------------- | -| `lat` | `double` | Latitude in decimal degrees (-90 to 90) | -| `lng` | `double` | Longitude in decimal degrees (-180 to 180) | -| **Returns** | `double` | Bearing in degrees clockwise from north (0-360) | - -Throws `RangeError` if coordinates are out of bounds. - -### `compassDir(bearing)` - -Eight-point compass abbreviation: N, NE, E, SE, S, SW, W, NW. - -### `compassName(bearing)` - -Full compass name: North, Northeast, East, Southeast, South, Southwest, West, Northwest. - -### `qiblaGreatCircle(lat, lng, [steps])` - -Generates waypoints along the great circle from (lat, lng) to the Ka'bah using spherical linear interpolation (Slerp). Returns `steps + 1` points (default: 121). - -Useful for drawing Qibla direction lines on maps. - -### `distanceKm(lat1, lng1, lat2, lng2)` - -Haversine distance between two points in kilometers (spherical Earth approximation, R = 6,371 km). - -### Constants - -| Name | Value | Description | -| -------------- | --------- | -------------------------------------- | -| `kaabaLat` | 21.422511 | Ka'bah center latitude (degrees north) | -| `kaabaLng` | 39.826150 | Ka'bah center longitude (degrees east) | -| `earthRadiusKm`| 6371 | WGS-84 volumetric mean radius | +Full reference: [API Reference](https://github.com/acamarata/qibla-dart/wiki/API-Reference). ## Architecture -All calculations use the forward azimuth formula from spherical trigonometry. Great-circle paths use spherical linear interpolation (Slerp). Distance uses the haversine formula. The Ka'bah coordinates are fixed constants verified against published GPS surveys. +All calculations use the forward azimuth formula from spherical trigonometry. Great-circle paths use Slerp. Distance uses the haversine formula. Ka'bah coordinates are fixed constants verified against published GPS surveys. ## Compatibility -Dart 3.7+. Works with Flutter and standalone Dart applications. +Dart 3.7+. Works with Flutter and standalone Dart. ## Related -- [qibla](https://www.npmjs.com/package/qibla) (npm) - The TypeScript version of this package. -- [pray-calc](https://github.com/acamarata/pray-calc) - Islamic prayer times calculator. - -## Acknowledgments - -Ka'bah coordinates verified against published GPS surveys and cross-checked with satellite imagery. Spherical trigonometry formulas follow the standard forward azimuth derivation used in aviation and geodesy. +- [qibla](https://www.npmjs.com/package/@acamarata/qibla) (npm) - The TypeScript version of this package. +- [pray-calc-dart](https://github.com/acamarata/pray-calc-dart) - Islamic prayer times calculator for Dart. ## License diff --git a/lib/src/qibla.dart b/lib/src/qibla.dart index 90ef94e..4e4ec1a 100644 --- a/lib/src/qibla.dart +++ b/lib/src/qibla.dart @@ -4,7 +4,15 @@ /// the Ka'bah using the spherical law of cosines. Includes compass direction /// lookup, great-circle interpolation, and haversine distance. /// -/// Ka'bah coordinates sourced from verified GPS data. +/// Ka'bah coordinates sourced from verified GPS surveys. +/// +/// Example: +/// ```dart +/// import 'package:qibla/qibla.dart'; +/// +/// final bearing = qiblaAngle(40.7128, -74.006); // ~58.48 +/// print(compassDir(bearing)); // NE +/// ``` library; import 'dart:math'; @@ -48,6 +56,11 @@ const List _compassNames = [ /// /// Throws [RangeError] if latitude is outside [-90, 90] or longitude /// outside [-180, 180]. +/// +/// Example: +/// ```dart +/// final bearing = qiblaAngle(40.7128, -74.006); // ~58.48 (northeast) +/// ``` double qiblaAngle(double lat, double lng) { if (lat < -90 || lat > 90) { throw RangeError('Latitude must be between -90 and 90, got $lat'); @@ -69,6 +82,12 @@ double qiblaAngle(double lat, double lng) { /// [bearing] is the bearing in degrees (0-360). /// /// Returns a compass abbreviation: N, NE, E, SE, S, SW, W, or NW. +/// +/// Example: +/// ```dart +/// compassDir(58.5); // 'NE' +/// compassDir(180.0); // 'S' +/// ``` String compassDir(double bearing) { return _compassAbbr[(bearing / 45).round() % 8]; } @@ -77,7 +96,14 @@ String compassDir(double bearing) { /// /// [bearing] is the bearing in degrees (0-360). /// -/// Returns the full direction name (North, Northeast, etc.). +/// Returns the full direction name (North, Northeast, East, Southeast, +/// South, Southwest, West, or Northwest). +/// +/// Example: +/// ```dart +/// compassName(58.5); // 'Northeast' +/// compassName(270.0); // 'West' +/// ``` String compassName(double bearing) { return _compassNames[(bearing / 45).round() % 8]; } @@ -91,9 +117,17 @@ String compassName(double bearing) { /// [lng] is the origin longitude in decimal degrees. /// [steps] is the number of segments (default: 120, producing 121 points). /// -/// Returns a list of [lat, lng] pairs in degrees. +/// Returns a list of `[latitude, longitude]` pairs in decimal degrees. +/// The first element is the observer location; the last is the Ka'bah. /// /// Throws [RangeError] if coordinates are out of bounds. +/// +/// Example: +/// ```dart +/// final path = qiblaGreatCircle(51.5074, -0.1278); // 121 points London -> Makkah +/// print(path.length); // 121 +/// print(path.first); // [51.5074, -0.1278] +/// ``` List> qiblaGreatCircle(double lat, double lng, [int steps = 120]) { if (lat < -90 || lat > 90) { throw RangeError('Latitude must be between -90 and 90, got $lat'); @@ -139,7 +173,14 @@ List> qiblaGreatCircle(double lat, double lng, [int steps = 120]) { /// [lat1], [lng1] define the first point in decimal degrees. /// [lat2], [lng2] define the second point in decimal degrees. /// -/// Returns the distance in kilometers (spherical Earth approximation). +/// Returns the distance in kilometers (spherical Earth approximation, +/// R = 6,371 km). +/// +/// Example: +/// ```dart +/// // Distance from New York to the Ka'bah +/// final km = distanceKm(40.7128, -74.006, kaabaLat, kaabaLng); // ~9634 +/// ``` double distanceKm(double lat1, double lng1, double lat2, double lng2) { final dLat = (lat2 - lat1) * _deg; final dLng = (lng2 - lng1) * _deg;