docs: portfolio polish — README, wiki, examples

This commit is contained in:
Aric Camarata 2026-05-30 18:35:21 -04:00
parent b5a3b8f8ef
commit 2717721ffa
3 changed files with 87 additions and 53 deletions

View file

@ -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.

View file

@ -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<List<double>>` | 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

View file

@ -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<String> _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<List<double>> 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<List<double>> 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;