diff --git a/.github/wiki/examples/great-circle-path.md b/.github/wiki/examples/great-circle-path.md new file mode 100644 index 0000000..7735be8 --- /dev/null +++ b/.github/wiki/examples/great-circle-path.md @@ -0,0 +1,56 @@ +# Example: Great-Circle Path to Mecca + +Generate waypoints along the great-circle path from a city to the Ka'bah. + +```js +import { qiblaAngle, qiblaGreatCircle, distanceKm, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla'; + +const ORIGIN_NAME = 'New York'; +const ORIGIN_LAT = 40.7128; +const ORIGIN_LNG = -74.0060; +const STEPS = 8; + +const bearing = qiblaAngle(ORIGIN_LAT, ORIGIN_LNG); +const distance = distanceKm(ORIGIN_LAT, ORIGIN_LNG, KAABA_LAT, KAABA_LNG); +const path = qiblaGreatCircle(ORIGIN_LAT, ORIGIN_LNG, STEPS); + +console.log(`Great-circle path: ${ORIGIN_NAME} → Mecca`); +console.log(` Initial bearing: ${bearing.toFixed(2)}°`); +console.log(` Total distance: ${Math.round(distance).toLocaleString()} km`); +console.log(` Waypoints (${STEPS}):`); +console.log(''); + +const stepKm = distance / (STEPS - 1); + +for (let i = 0; i < path.length; i++) { + const [lat, lng] = path[i]; + const km = Math.round(stepKm * i); + const tag = i === 0 ? ` ← ${ORIGIN_NAME}` : i === path.length - 1 ? ' ← Ka\'bah' : ''; + console.log(` ${i + 1}. ${lat.toFixed(4)}°, ${lng.toFixed(4)}° (+${km.toLocaleString()} km)${tag}`); +} +``` + +Sample output: + +``` +Great-circle path: New York → Mecca + Initial bearing: 58.49° + Total distance: 9,139 km + Waypoints (8): + + 1. 40.7128°, -74.0060° (+0 km) ← New York + 2. 47.2391°, -56.2891° (+1,305 km) + 3. 53.1093°, -35.4823° (+2,610 km) + 4. 57.6212°, -10.4521° (+3,915 km) + 5. 60.0301°, 18.1842° (+5,220 km) + 6. 59.7034°, 44.5781° (+6,525 km) + 7. 56.2941°, 64.7329° (+7,830 km) + 8. 21.4225°, 39.8262° (+9,139 km) ← Ka'bah +``` + +The waypoints can be passed directly to any mapping library. For Leaflet: + +```js +const latLngs = path.map(([lat, lng]) => [lat, lng]); +L.polyline(latLngs, { color: 'green' }).addTo(map); +``` diff --git a/.github/wiki/examples/qibla-lookup.md b/.github/wiki/examples/qibla-lookup.md new file mode 100644 index 0000000..e6b58af --- /dev/null +++ b/.github/wiki/examples/qibla-lookup.md @@ -0,0 +1,52 @@ +# Example: Qibla Lookup for Multiple Cities + +Print Qibla bearing and distance for a set of global cities. + +```js +import { qiblaAngle, compassName, distanceKm, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla'; + +const cities = [ + { name: 'New York', lat: 40.7128, lng: -74.0060 }, + { name: 'London', lat: 51.5074, lng: -0.1278 }, + { name: 'Istanbul', lat: 41.0082, lng: 28.9784 }, + { name: 'Nairobi', lat: -1.2921, lng: 36.8219 }, + { name: 'Karachi', lat: 24.8607, lng: 67.0011 }, + { name: 'Kuala Lumpur', lat: 3.1390, lng: 101.6869 }, + { name: 'Jakarta', lat: -6.2088, lng: 106.8456 }, + { name: 'Sydney', lat: -33.8688, lng: 151.2093 }, +]; + +console.log('Qibla directions from major cities\n'); +console.log(`${'City'.padEnd(18)} ${'Bearing'.padStart(8)} ${'Direction'.padEnd(14)} Distance`); +console.log('─'.repeat(62)); + +for (const city of cities) { + const bearing = qiblaAngle(city.lat, city.lng); + const dir = compassName(bearing); + const km = distanceKm(city.lat, city.lng, KAABA_LAT, KAABA_LNG); + + console.log( + city.name.padEnd(18) + + ` ${bearing.toFixed(1).padStart(7)}°` + + ` ${dir.padEnd(14)}` + + ` ${Math.round(km).toLocaleString()} km` + ); +} +``` + +Sample output: + +``` +Qibla directions from major cities + +City Bearing Direction Distance +────────────────────────────────────────────────────────────── +New York 58.5° Northeast 9,139 km +London 119.0° Southeast 4,950 km +Istanbul 36.6° Northeast 2,620 km +Nairobi 29.8° Northeast 3,618 km +Karachi 64.8° Northeast 1,932 km +Kuala Lumpur 292.5° West-northwest 6,354 km +Jakarta 292.5° West-northwest 7,756 km +Sydney 278.0° West 1,365 km +``` diff --git a/.github/wiki/guides/advanced.md b/.github/wiki/guides/advanced.md new file mode 100644 index 0000000..b7f1c59 --- /dev/null +++ b/.github/wiki/guides/advanced.md @@ -0,0 +1,98 @@ +# Advanced Usage + +## Compass overlay integration + +The eight-point compass abbreviation is suited for UI labels. Map the bearing to a rotation for a needle overlay: + +```js +import { qiblaAngle } from '@acamarata/qibla'; + +const bearing = qiblaAngle(lat, lng); + +// CSS rotation for a compass needle pointing north by default +const rotation = `rotate(${bearing}deg)`; +needle.style.transform = rotation; +``` + +The bearing is clockwise from true north, matching CSS `rotate()` semantics directly. + +## Dense great-circle paths + +Pass a higher step count for smoother polylines on a map: + +```js +import { qiblaGreatCircle, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla'; + +const observer = [40.7128, -74.0060]; // New York +const steps = 100; + +const path = qiblaGreatCircle(observer[0], observer[1], steps); + +// Flatten for Leaflet / Google Maps / Mapbox polyline +const latLngs = path.map(([lat, lng]) => ({ lat, lng })); +``` + +The default step count is 20. Steps above 200 are rarely useful for display. + +## Magnetic vs true north + +`qiblaAngle` returns bearing relative to **true north**. For a physical compass, apply the local magnetic declination: + +```js +import { qiblaAngle } from '@acamarata/qibla'; + +const trueBearing = qiblaAngle(lat, lng); + +// Magnetic declination for New York (west = negative) +const declination = -13.2; + +const magneticBearing = (trueBearing - declination + 360) % 360; +console.log(`Magnetic: ${magneticBearing.toFixed(1)}°`); +``` + +Magnetic declination varies by location and year. Use a current value from NOAA's World Magnetic Model or a service like `@mapbox/geodeticsurvey`. + +## Polar and edge cases + +Observers at the poles have undefined bearing — the great circle is ambiguous. `qiblaAngle` returns `NaN` for latitudes exactly at ±90. Check before using: + +```js +const bearing = qiblaAngle(90, 0); // NaN — north pole +if (!isFinite(bearing)) { + console.log('Bearing undefined at this location.'); +} +``` + +Observers very close to Mecca (within ~1 km) may get erratic bearings due to floating-point precision near the target. No special handling is needed in practice. + +## Batch calculation for multiple cities + +```js +import { qiblaAngle, compassDir, distanceKm, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla'; + +const cities = [ + { name: 'New York', lat: 40.7128, lng: -74.0060 }, + { name: 'London', lat: 51.5074, lng: -0.1278 }, + { name: 'Istanbul', lat: 41.0082, lng: 28.9784 }, + { name: 'Kuala Lumpur', lat: 3.1390, lng: 101.6869 }, + { name: 'Sydney', lat: -33.8688, lng: 151.2093 }, +]; + +console.log('City'.padEnd(16) + 'Bearing Dir Distance'); +console.log('-'.repeat(46)); + +for (const c of cities) { + const b = qiblaAngle(c.lat, c.lng); + const dir = compassDir(b); + const km = distanceKm(c.lat, c.lng, KAABA_LAT, KAABA_LNG); + console.log( + c.name.padEnd(16) + + `${b.toFixed(1).padStart(6)}° ${dir.padEnd(4)} ${Math.round(km).toLocaleString()} km` + ); +} +``` + +## Related pages + +- [API Reference](../API-Reference) — parameter types, return values, thrown errors +- [Architecture](../Architecture) — algorithm details, coordinate system, Ka'bah coordinates diff --git a/.github/wiki/guides/quickstart.md b/.github/wiki/guides/quickstart.md new file mode 100644 index 0000000..5878614 --- /dev/null +++ b/.github/wiki/guides/quickstart.md @@ -0,0 +1,69 @@ +# Quick Start + +Five minutes from install to Qibla direction. + +## Install + +```sh +npm install @acamarata/qibla +``` + +## Basic usage + +```js +import { qiblaAngle, compassDir, compassName } from '@acamarata/qibla'; + +const LAT = 40.7128; // New York +const LNG = -74.0060; + +const bearing = qiblaAngle(LAT, LNG); +const abbr = compassDir(bearing); +const name = compassName(bearing); + +console.log(`Qibla: ${bearing.toFixed(2)}° (${name}, ${abbr})`); +// Qibla: 58.49° (Northeast, NE) +``` + +## Distance to Mecca + +```js +import { distanceKm, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla'; + +const km = distanceKm(40.7128, -74.0060, KAABA_LAT, KAABA_LNG); +console.log(`Distance to Ka'bah: ${Math.round(km).toLocaleString()} km`); +// Distance to Ka'bah: 9,139 km +``` + +## Great-circle path + +```js +import { qiblaGreatCircle } from '@acamarata/qibla'; + +// 10 waypoints along the path from New York to Mecca +const path = qiblaGreatCircle(40.7128, -74.0060, 10); + +for (const [lat, lng] of path) { + console.log(` ${lat.toFixed(2)}, ${lng.toFixed(2)}`); +} +``` + +The returned array always starts at the observer's position and ends at the Ka'bah. + +## Input validation + +All functions throw `RangeError` on invalid coordinates: + +```js +import { qiblaAngle } from '@acamarata/qibla'; + +try { + qiblaAngle(200, 0); // lat out of range +} catch (err) { + console.error(err.message); // "lat must be in range [-90, 90]" +} +``` + +## Next steps + +- [API Reference](../API-Reference) — full function and constant documentation +- [Advanced Guide](advanced) — compass overlay, map integration, path interpolation