mirror of
https://github.com/acamarata/qibla-dart.git
synced 2026-07-03 04:10:40 +00:00
Compare commits
7 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d89c0e6350 | ||
|
|
5efc56bbea | ||
|
|
2717721ffa | ||
|
|
b5a3b8f8ef | ||
|
|
238ad4e858 | ||
|
|
cc3c227d2d | ||
|
|
51894b95a0 |
14 changed files with 430 additions and 61 deletions
|
|
@ -1 +0,0 @@
|
||||||
CLAUDE.md
|
|
||||||
34
.github/wiki/CODE_OF_CONDUCT.md
vendored
Normal file
34
.github/wiki/CODE_OF_CONDUCT.md
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as contributors and maintainers pledge to make participation in this project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment:
|
||||||
|
|
||||||
|
- Using welcoming and inclusive language
|
||||||
|
- Being respectful of differing viewpoints and experiences
|
||||||
|
- Accepting constructive criticism gracefully
|
||||||
|
- Focusing on what is best for the community
|
||||||
|
- Showing empathy toward other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior:
|
||||||
|
|
||||||
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
- Public or private harassment
|
||||||
|
- Publishing others' private information without explicit permission
|
||||||
|
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable behavior. They will take appropriate and fair corrective action in response to any behavior they deem inappropriate, threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer at alisalaah@gmail.com. All complaints will be reviewed and investigated. The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
|
||||||
24
.github/wiki/Home.md
vendored
24
.github/wiki/Home.md
vendored
|
|
@ -1,21 +1,29 @@
|
||||||
# qibla
|
# qibla
|
||||||
|
|
||||||
Qibla direction, great-circle path, and haversine distance for Dart and Flutter. Pure math, zero dependencies.
|
Qibla direction, great-circle path, and haversine distance for Dart and Flutter. Computes the bearing to the Ka'bah from any point on Earth. Pure math, zero dependencies.
|
||||||
|
|
||||||
## Quick Start
|
## Install
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
qibla: ^1.0.1
|
||||||
|
```
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
import 'package:qibla/qibla.dart';
|
import 'package:qibla/qibla.dart';
|
||||||
|
|
||||||
final bearing = qiblaAngle(40.7128, -74.006);
|
final bearing = qiblaAngle(40.7128, -74.006);
|
||||||
print(bearing); // ~58.48
|
print(bearing); // ~58.48
|
||||||
print(compassDir(bearing)); // NE
|
print(compassDir(bearing)); // NE
|
||||||
|
|
||||||
final km = distanceKm(40.7128, -74.006, kaabaLat, kaabaLng);
|
final km = distanceKm(40.7128, -74.006, kaabaLat, kaabaLng);
|
||||||
print(km); // ~9634
|
print(km); // ~9634
|
||||||
```
|
```
|
||||||
|
|
||||||
## Pages
|
## Contents
|
||||||
|
|
||||||
- [API Reference](API-Reference): Full function and constant reference
|
- [Quickstart Guide](guides/quickstart) — install, first call, compass directions
|
||||||
- [Architecture](Architecture): Spherical trigonometry and Slerp implementation
|
- [Advanced Usage](guides/advanced) — great-circle paths, custom interpolation
|
||||||
|
- [API Reference](API-Reference) — full function and constant reference
|
||||||
|
- [Examples](examples/basic-usage) — real-world snippets
|
||||||
|
- [Contributing](CONTRIBUTING)
|
||||||
|
|
|
||||||
27
.github/wiki/SECURITY.md
vendored
Normal file
27
.github/wiki/SECURITY.md
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Security
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
`qibla` is a pure-math library with no network access, no file I/O, and no external dependencies. The attack surface is limited to the mathematical functions themselves.
|
||||||
|
|
||||||
|
The main concern is input validation: `qiblaAngle` and `qiblaGreatCircle` throw `RangeError` on out-of-bounds coordinates. If you pass untrusted input to these functions, catch the error.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you discover a security issue (for example, a case where malformed input causes unexpected behavior beyond the documented `RangeError`), please report it privately before filing a public issue.
|
||||||
|
|
||||||
|
**Contact:** alisalaah@gmail.com
|
||||||
|
|
||||||
|
Include:
|
||||||
|
|
||||||
|
1. A description of the vulnerability
|
||||||
|
2. Steps to reproduce it
|
||||||
|
3. The version of `qibla` where you observed the issue
|
||||||
|
4. Any suggested fix if you have one
|
||||||
|
|
||||||
|
You can expect an acknowledgment within 48 hours and a resolution or status update within 7 days.
|
||||||
|
|
||||||
|
## Known Limitations
|
||||||
|
|
||||||
|
- `distanceKm` uses a spherical Earth model (R = 6,371 km). It does not account for Earth's ellipsoidal shape. For high-precision geodesy, use a WGS-84 library.
|
||||||
|
- Ka'bah coordinates are fixed constants. They will not change unless there is a documented scholarly correction to the GPS position.
|
||||||
3
.github/wiki/_Footer.md
vendored
Normal file
3
.github/wiki/_Footer.md
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[](https://pub.dev/packages/qibla) [](https://github.com/acamarata/qibla-dart/blob/main/LICENSE)
|
||||||
|
|
||||||
|
[pub.dev](https://pub.dev/packages/qibla) | [GitHub](https://github.com/acamarata/qibla-dart) | [Issues](https://github.com/acamarata/qibla-dart/issues)
|
||||||
20
.github/wiki/_Sidebar.md
vendored
Normal file
20
.github/wiki/_Sidebar.md
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
## qibla
|
||||||
|
|
||||||
|
- [Home](Home)
|
||||||
|
- [API Reference](API-Reference)
|
||||||
|
|
||||||
|
**Guides**
|
||||||
|
- [Quickstart](guides/quickstart)
|
||||||
|
- [Advanced Usage](guides/advanced)
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
- [Basic Usage](examples/basic-usage)
|
||||||
|
- [Flutter Integration](examples/flutter-integration)
|
||||||
|
|
||||||
|
**Project**
|
||||||
|
- [Code of Conduct](CODE_OF_CONDUCT)
|
||||||
|
- [Security](SECURITY)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[pub.dev](https://pub.dev/packages/qibla) | [GitHub](https://github.com/acamarata/qibla-dart)
|
||||||
67
.github/wiki/examples/basic-usage.md
vendored
Normal file
67
.github/wiki/examples/basic-usage.md
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Basic Usage Examples
|
||||||
|
|
||||||
|
## Qibla for Multiple Cities
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:qibla/qibla.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final cities = [
|
||||||
|
('New York', 40.7128, -74.0060),
|
||||||
|
('London', 51.5074, -0.1278),
|
||||||
|
('Istanbul', 41.0082, 28.9784),
|
||||||
|
('Jakarta', -6.2088, 106.8456),
|
||||||
|
('Cape Town', -33.9249, 18.4241),
|
||||||
|
];
|
||||||
|
|
||||||
|
print('City Bearing Direction Distance');
|
||||||
|
print('${'─' * 54}');
|
||||||
|
|
||||||
|
for (final (city, lat, lng) in cities) {
|
||||||
|
final bearing = qiblaAngle(lat, lng);
|
||||||
|
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',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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 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++) {
|
||||||
|
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.
|
||||||
92
.github/wiki/examples/flutter-integration.md
vendored
Normal file
92
.github/wiki/examples/flutter-integration.md
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
# Flutter Integration
|
||||||
|
|
||||||
|
This example shows how to use `qibla` in a Flutter widget. It reads the device's current location using `geolocator`, computes the Qibla bearing, and displays the result.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Add to `pubspec.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
qibla: ^1.0.0
|
||||||
|
geolocator: ^14.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## QiblaCard Widget
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:qibla/qibla.dart';
|
||||||
|
|
||||||
|
class QiblaCard extends StatefulWidget {
|
||||||
|
const QiblaCard({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<QiblaCard> createState() => _QiblaCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QiblaCardState extends State<QiblaCard> {
|
||||||
|
double? _bearing;
|
||||||
|
String? _error;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadQibla();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadQibla() async {
|
||||||
|
try {
|
||||||
|
final permission = await Geolocator.requestPermission();
|
||||||
|
if (permission == LocationPermission.denied ||
|
||||||
|
permission == LocationPermission.deniedForever) {
|
||||||
|
setState(() => _error = 'Location permission denied.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final pos = await Geolocator.getCurrentPosition();
|
||||||
|
final bearing = qiblaAngle(pos.latitude, pos.longitude);
|
||||||
|
setState(() => _bearing = bearing);
|
||||||
|
} catch (e) {
|
||||||
|
setState(() => _error = e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (_error != null) {
|
||||||
|
return Text('Error: $_error');
|
||||||
|
}
|
||||||
|
if (_bearing == null) {
|
||||||
|
return const CircularProgressIndicator();
|
||||||
|
}
|
||||||
|
|
||||||
|
final dir = compassName(_bearing!);
|
||||||
|
return Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${_bearing!.toStringAsFixed(1)}°',
|
||||||
|
style: Theme.of(context).textTheme.displayMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(dir, style: Theme.of(context).textTheme.titleMedium),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text('Qibla bearing from your location'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `qiblaAngle` and `compassName` are pure functions. They do not require any platform channel or plugin; the only async work is the location request.
|
||||||
|
- The bearing is in degrees clockwise from true north. To rotate a compass needle image, use a `Transform.rotate` widget with `angle = bearing * (pi / 180)`.
|
||||||
|
- For a live compass, combine the bearing with a magnetometer reading from `flutter_compass` to subtract the device heading.
|
||||||
50
.github/wiki/guides/advanced.md
vendored
Normal file
50
.github/wiki/guides/advanced.md
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Advanced Usage
|
||||||
|
|
||||||
|
## Great-Circle Path
|
||||||
|
|
||||||
|
Compute N interpolated waypoints along the great-circle route from your location to the Ka'bah:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:qibla/qibla.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// 5 waypoints from New York to Makkah
|
||||||
|
final path = greatCirclePath(40.7128, -74.0060, kaabaLat, kaabaLng, 5);
|
||||||
|
|
||||||
|
for (final point in path) {
|
||||||
|
print('${point.latitude.toStringAsFixed(4)}, ${point.longitude.toStringAsFixed(4)}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The path uses spherical linear interpolation (Slerp) and returns `n` evenly spaced coordinates including the start and end points.
|
||||||
|
|
||||||
|
## Custom Destination
|
||||||
|
|
||||||
|
`distanceKm` and `greatCirclePath` work with any two points, not just the Ka'bah:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// Distance between two mosques
|
||||||
|
final km = distanceKm(51.5074, -0.1278, 40.7128, -74.0060);
|
||||||
|
print('London to New York: ${km.toStringAsFixed(0)} km');
|
||||||
|
|
||||||
|
// Path from Istanbul to Makkah
|
||||||
|
final path = greatCirclePath(41.0082, 28.9784, kaabaLat, kaabaLng, 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flutter Compass Widget Integration
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:qibla/qibla.dart';
|
||||||
|
|
||||||
|
// In a StatefulWidget with device compass heading:
|
||||||
|
Widget buildQiblaArrow(double deviceHeading, double lat, double lng) {
|
||||||
|
final qibla = qiblaAngle(lat, lng);
|
||||||
|
final arrowRotation = (qibla - deviceHeading) * (pi / 180);
|
||||||
|
|
||||||
|
return Transform.rotate(
|
||||||
|
angle: arrowRotation,
|
||||||
|
child: const Icon(Icons.navigation, size: 48),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
50
.github/wiki/guides/quickstart.md
vendored
Normal file
50
.github/wiki/guides/quickstart.md
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Quickstart
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Add to `pubspec.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
qibla: ^1.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `dart pub get`.
|
||||||
|
|
||||||
|
## Qibla Bearing
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:qibla/qibla.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// New York
|
||||||
|
final bearing = qiblaAngle(40.7128, -74.0060);
|
||||||
|
print('Bearing to Ka''bah: ${bearing.toStringAsFixed(2)}°');
|
||||||
|
print('Compass direction: ${compassDir(bearing)}');
|
||||||
|
print('Full direction: ${compassPoint(bearing)}');
|
||||||
|
|
||||||
|
// Distance to Ka'bah
|
||||||
|
final km = distanceKm(40.7128, -74.0060, kaabaLat, kaabaLng);
|
||||||
|
print('Distance: ${km.toStringAsFixed(0)} km');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compass Directions
|
||||||
|
|
||||||
|
`compassDir` returns an 8-point abbreviation (N, NE, E, SE, S, SW, W, NW).
|
||||||
|
`compassPoint` returns the full name (North, Northeast, East, etc.).
|
||||||
|
|
||||||
|
```dart
|
||||||
|
final bearing = qiblaAngle(lat, lng);
|
||||||
|
final short = compassDir(bearing); // "NE"
|
||||||
|
final full = compassPoint(bearing); // "Northeast"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ka'bah Coordinates
|
||||||
|
|
||||||
|
The Ka'bah coordinates are exported as constants:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
print(kaabaLat); // 21.4225
|
||||||
|
print(kaabaLng); // 39.8262
|
||||||
|
```
|
||||||
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -3,7 +3,13 @@
|
||||||
## [1.0.1] - 2026-05-25
|
## [1.0.1] - 2026-05-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Initial public release of Dart Qibla direction library
|
- Surface-area parity audit with qibla-js counterpart.
|
||||||
- `calcQibla(lat, lng)` function returning bearing in degrees
|
- Repository structure and documentation alignment.
|
||||||
- 48 tests passing across edge cases
|
- Publisher field cleanup in pubspec.yaml.
|
||||||
- Pure Dart implementation with no native dependencies
|
|
||||||
|
## [1.0.0] - 2026-05-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial release: Qibla direction, great-circle distance, and haversine math for Dart and Flutter.
|
||||||
|
- Zero dependencies.
|
||||||
|
- Exports: `qiblaAngle`, `compassDir`, `distanceKm`, `kaabaLat`, `kaabaLng`.
|
||||||
|
|
|
||||||
59
README.md
59
README.md
|
|
@ -3,6 +3,7 @@
|
||||||
[](https://pub.dev/packages/qibla)
|
[](https://pub.dev/packages/qibla)
|
||||||
[](https://github.com/acamarata/qibla-dart/actions/workflows/ci.yml)
|
[](https://github.com/acamarata/qibla-dart/actions/workflows/ci.yml)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
[](https://github.com/acamarata/qibla-dart/wiki)
|
||||||
|
|
||||||
Qibla direction, great-circle path, and haversine distance for Dart and Flutter. Pure math, zero dependencies.
|
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
|
// Bearing from New York to the Ka'bah
|
||||||
final bearing = qiblaAngle(40.7128, -74.006);
|
final bearing = qiblaAngle(40.7128, -74.006);
|
||||||
print(bearing); // ~58.48
|
print(bearing); // ~58.48
|
||||||
print(compassDir(bearing)); // NE
|
print(compassDir(bearing)); // NE
|
||||||
|
|
||||||
// Distance in kilometers
|
// Distance in kilometers
|
||||||
|
|
@ -30,60 +31,30 @@ print(km); // ~9634
|
||||||
|
|
||||||
## API
|
## 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 |
|
Full reference: [API Reference](https://github.com/acamarata/qibla-dart/wiki/API-Reference).
|
||||||
| ------------ | -------- | ----------------------------------------------- |
|
|
||||||
| `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 |
|
|
||||||
|
|
||||||
## Architecture
|
## 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
|
## Compatibility
|
||||||
|
|
||||||
Dart 3.7+. Works with Flutter and standalone Dart applications.
|
Dart 3.7+. Works with Flutter and standalone Dart.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
- [qibla](https://www.npmjs.com/package/qibla) (npm) - The TypeScript version of this package.
|
- [qibla](https://www.npmjs.com/package/@acamarata/qibla) (npm) - The TypeScript version of this package.
|
||||||
- [pray-calc](https://github.com/acamarata/pray-calc) - Islamic prayer times calculator.
|
- [pray-calc-dart](https://github.com/acamarata/pray-calc-dart) - Islamic prayer times calculator for Dart.
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,15 @@
|
||||||
/// the Ka'bah using the spherical law of cosines. Includes compass direction
|
/// the Ka'bah using the spherical law of cosines. Includes compass direction
|
||||||
/// lookup, great-circle interpolation, and haversine distance.
|
/// 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;
|
library;
|
||||||
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
@ -48,6 +56,11 @@ const List<String> _compassNames = [
|
||||||
///
|
///
|
||||||
/// Throws [RangeError] if latitude is outside [-90, 90] or longitude
|
/// Throws [RangeError] if latitude is outside [-90, 90] or longitude
|
||||||
/// outside [-180, 180].
|
/// outside [-180, 180].
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final bearing = qiblaAngle(40.7128, -74.006); // ~58.48 (northeast)
|
||||||
|
/// ```
|
||||||
double qiblaAngle(double lat, double lng) {
|
double qiblaAngle(double lat, double lng) {
|
||||||
if (lat < -90 || lat > 90) {
|
if (lat < -90 || lat > 90) {
|
||||||
throw RangeError('Latitude must be between -90 and 90, got $lat');
|
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).
|
/// [bearing] is the bearing in degrees (0-360).
|
||||||
///
|
///
|
||||||
/// Returns a compass abbreviation: N, NE, E, SE, S, SW, W, or NW.
|
/// 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) {
|
String compassDir(double bearing) {
|
||||||
return _compassAbbr[(bearing / 45).round() % 8];
|
return _compassAbbr[(bearing / 45).round() % 8];
|
||||||
}
|
}
|
||||||
|
|
@ -77,7 +96,14 @@ String compassDir(double bearing) {
|
||||||
///
|
///
|
||||||
/// [bearing] is the bearing in degrees (0-360).
|
/// [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) {
|
String compassName(double bearing) {
|
||||||
return _compassNames[(bearing / 45).round() % 8];
|
return _compassNames[(bearing / 45).round() % 8];
|
||||||
}
|
}
|
||||||
|
|
@ -91,9 +117,17 @@ String compassName(double bearing) {
|
||||||
/// [lng] is the origin longitude in decimal degrees.
|
/// [lng] is the origin longitude in decimal degrees.
|
||||||
/// [steps] is the number of segments (default: 120, producing 121 points).
|
/// [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.
|
/// 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]) {
|
List<List<double>> qiblaGreatCircle(double lat, double lng, [int steps = 120]) {
|
||||||
if (lat < -90 || lat > 90) {
|
if (lat < -90 || lat > 90) {
|
||||||
throw RangeError('Latitude must be between -90 and 90, got $lat');
|
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.
|
/// [lat1], [lng1] define the first point in decimal degrees.
|
||||||
/// [lat2], [lng2] define the second 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) {
|
double distanceKm(double lat1, double lng1, double lat2, double lng2) {
|
||||||
final dLat = (lat2 - lat1) * _deg;
|
final dLat = (lat2 - lat1) * _deg;
|
||||||
final dLng = (lng2 - lng1) * _deg;
|
final dLng = (lng2 - lng1) * _deg;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ description: >
|
||||||
Flutter. Pure math, zero dependencies. Computes bearing to the Ka'bah
|
Flutter. Pure math, zero dependencies. Computes bearing to the Ka'bah
|
||||||
from any point on Earth.
|
from any point on Earth.
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
|
homepage: https://github.com/acamarata/qibla-dart
|
||||||
repository: https://github.com/acamarata/qibla-dart
|
repository: https://github.com/acamarata/qibla-dart
|
||||||
issue_tracker: https://github.com/acamarata/qibla-dart/issues
|
issue_tracker: https://github.com/acamarata/qibla-dart/issues
|
||||||
topics:
|
topics:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue