@ue-too/border 0.9.4 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,486 @@
1
- # border
1
+ # @ue-too/border
2
2
 
3
- This is a library that provides utilities for working with and geographic projections.
4
- Following the tutorial from [Chris Veness](https://www.movable-type.co.uk/scripts/latlong.html).
3
+ Geodesy and map projection library for TypeScript.
5
4
 
6
- Detailed information would be added in the future.
5
+ [![npm version](https://img.shields.io/npm/v/@ue-too/border.svg)](https://www.npmjs.com/package/@ue-too/border)
6
+ [![license](https://img.shields.io/npm/l/@ue-too/border.svg)](https://github.com/ue-too/ue-too/blob/main/LICENSE.txt)
7
+
8
+ ## Overview
9
+
10
+ `@ue-too/border` provides geodesy calculations and map projections for working with geographic coordinates on the Earth's surface. Based on algorithms from [Movable Type Scripts](https://www.movable-type.co.uk/scripts/latlong.html) by Chris Veness, this library includes utilities for great circle navigation, rhumb line navigation, and coordinate transformations.
11
+
12
+ ### Key Features
13
+
14
+ - **Great Circle Navigation**: Shortest path calculations on a sphere
15
+ - **Rhumb Line Navigation**: Constant bearing paths for easier navigation
16
+ - **Map Projections**: Mercator and orthographic projections with inverse transformations
17
+ - **Distance Calculations**: Accurate distances between geographic coordinates
18
+ - **Bearing Calculations**: Initial and constant bearings for navigation
19
+ - **Intermediate Points**: Find points along paths
20
+ - **Midpoint Calculation**: Find midpoints between coordinates
21
+
22
+ ## Installation
23
+
24
+ Using Bun:
25
+ ```bash
26
+ bun add @ue-too/border
27
+ ```
28
+
29
+ Using npm:
30
+ ```bash
31
+ npm install @ue-too/border
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ Here's a simple example calculating distance and bearing between two cities:
37
+
38
+ ```typescript
39
+ import {
40
+ greatCircleDistance,
41
+ initialBearingOfGreatCircle
42
+ } from '@ue-too/border';
43
+
44
+ // New York to London
45
+ const nyc = { latitude: 40.7128, longitude: -74.0060 };
46
+ const london = { latitude: 51.5074, longitude: -0.1278 };
47
+
48
+ // Calculate great circle distance (in meters)
49
+ const distance = greatCircleDistance(nyc, london);
50
+ console.log('Distance:', (distance / 1000).toFixed(0), 'km'); // ~5570 km
51
+
52
+ // Calculate initial bearing (in degrees)
53
+ const bearing = initialBearingOfGreatCircle(nyc, london);
54
+ console.log('Bearing:', bearing.toFixed(1), '°'); // ~51.4° (northeast)
55
+ ```
56
+
57
+ ## Core Concepts
58
+
59
+ ### Great Circles
60
+
61
+ A great circle is the shortest path between two points on a sphere (like the Earth). Airlines typically fly great circle routes to minimize distance.
62
+
63
+ - **Advantages**: Shortest distance, fuel-efficient
64
+ - **Characteristics**: Bearing changes continuously along the path (except when traveling due north/south or along the equator)
65
+
66
+ ### Rhumb Lines
67
+
68
+ A rhumb line (loxodrome) is a path of constant bearing. While slightly longer than great circles, they're easier to navigate.
69
+
70
+ - **Advantages**: Constant bearing, simpler navigation
71
+ - **Characteristics**: Spirals toward poles, longer distance than great circles (except along equator or meridians)
72
+
73
+ ### Map Projections
74
+
75
+ Map projections transform spherical coordinates (latitude/longitude) to flat 2D coordinates for display.
76
+
77
+ - **Mercator**: Preserves angles, distorts size at high latitudes
78
+ - **Orthographic**: Perspective view from space, shows one hemisphere
79
+
80
+ ## Core APIs
81
+
82
+ ### Great Circle Functions
83
+
84
+ #### `greatCircleDistance(from, to)`
85
+
86
+ Calculate the great circle distance between two points.
87
+
88
+ ```typescript
89
+ function greatCircleDistance(
90
+ from: GeoCoord,
91
+ to: GeoCoord
92
+ ): number; // Returns distance in meters
93
+ ```
94
+
95
+ **Example:**
96
+ ```typescript
97
+ const distance = greatCircleDistance(
98
+ { latitude: 51.5074, longitude: -0.1278 }, // London
99
+ { latitude: 48.8566, longitude: 2.3522 } // Paris
100
+ );
101
+ console.log(distance / 1000, 'km'); // ~344 km
102
+ ```
103
+
104
+ #### `initialBearingOfGreatCircle(from, to)`
105
+
106
+ Calculate the initial bearing (direction) for a great circle path.
107
+
108
+ ```typescript
109
+ function initialBearingOfGreatCircle(
110
+ from: GeoCoord,
111
+ to: GeoCoord
112
+ ): number; // Returns bearing in degrees (0-360)
113
+ ```
114
+
115
+ **Example:**
116
+ ```typescript
117
+ const bearing = initialBearingOfGreatCircle(
118
+ { latitude: 40.7128, longitude: -74.0060 }, // NYC
119
+ { latitude: 51.5074, longitude: -0.1278 } // London
120
+ );
121
+ console.log(bearing, '°'); // ~51.4° (northeast)
122
+ ```
123
+
124
+ #### `destinationFromOriginOnGreatCircle(origin, bearing, distance)`
125
+
126
+ Find the destination point given origin, bearing, and distance.
127
+
128
+ ```typescript
129
+ function destinationFromOriginOnGreatCircle(
130
+ origin: GeoCoord,
131
+ bearing: number, // Degrees
132
+ distance: number // Meters
133
+ ): GeoCoord;
134
+ ```
135
+
136
+ **Example:**
137
+ ```typescript
138
+ const start = { latitude: 51.5074, longitude: -0.1278 };
139
+ const destination = destinationFromOriginOnGreatCircle(start, 90, 100000);
140
+ console.log('100km east:', destination);
141
+ ```
142
+
143
+ #### `midpointOnGreatCircle(from, to)`
144
+
145
+ Find the midpoint along a great circle path.
146
+
147
+ ```typescript
148
+ function midpointOnGreatCircle(
149
+ from: GeoCoord,
150
+ to: GeoCoord
151
+ ): GeoCoord;
152
+ ```
153
+
154
+ #### `intermediatePointOnGreatCircle(from, to, fraction)`
155
+
156
+ Find a point at a given fraction along the great circle path.
157
+
158
+ ```typescript
159
+ function intermediatePointOnGreatCircle(
160
+ from: GeoCoord,
161
+ to: GeoCoord,
162
+ fraction: number // 0.0 to 1.0
163
+ ): GeoCoord;
164
+ ```
165
+
166
+ **Example:**
167
+ ```typescript
168
+ // Find the point 25% of the way from NYC to London
169
+ const point = intermediatePointOnGreatCircle(nyc, london, 0.25);
170
+ ```
171
+
172
+ ### Rhumb Line Functions
173
+
174
+ #### `rhumbDistance(from, to)`
175
+
176
+ Calculate the rhumb line distance (constant bearing path).
177
+
178
+ ```typescript
179
+ function rhumbDistance(
180
+ from: GeoCoord,
181
+ to: GeoCoord
182
+ ): number; // Returns distance in meters
183
+ ```
184
+
185
+ #### `rhumbBearing(from, to)`
186
+
187
+ Calculate the constant bearing for a rhumb line.
188
+
189
+ ```typescript
190
+ function rhumbBearing(
191
+ from: GeoCoord,
192
+ to: GeoCoord
193
+ ): number; // Returns bearing in degrees (0-360)
194
+ ```
195
+
196
+ #### `destinationFromOriginOnRhumbLine(origin, bearing, distance)`
197
+
198
+ Find the destination on a rhumb line given origin, bearing, and distance.
199
+
200
+ ```typescript
201
+ function destinationFromOriginOnRhumbLine(
202
+ origin: GeoCoord,
203
+ bearing: number, // Degrees
204
+ distance: number // Meters
205
+ ): GeoCoord;
206
+ ```
207
+
208
+ #### `midpointOnRhumbLine(from, to)`
209
+
210
+ Find the midpoint along a rhumb line.
211
+
212
+ ```typescript
213
+ function midpointOnRhumbLine(
214
+ from: GeoCoord,
215
+ to: GeoCoord
216
+ ): GeoCoord;
217
+ ```
218
+
219
+ ### Map Projection Functions
220
+
221
+ #### `mercatorProjection(coord)`
222
+
223
+ Convert geographic coordinates to Mercator projection.
224
+
225
+ ```typescript
226
+ function mercatorProjection(
227
+ coord: GeoCoord
228
+ ): Point; // Returns {x, y} in normalized coordinates
229
+ ```
230
+
231
+ **Example:**
232
+ ```typescript
233
+ const point = mercatorProjection({ latitude: 51.5074, longitude: -0.1278 });
234
+ console.log('Mercator coordinates:', point);
235
+ ```
236
+
237
+ #### `inverseMercatorProjection(point)`
238
+
239
+ Convert Mercator coordinates back to geographic.
240
+
241
+ ```typescript
242
+ function inverseMercatorProjection(
243
+ point: Point
244
+ ): GeoCoord; // Returns {latitude, longitude}
245
+ ```
246
+
247
+ #### `orthoProjection(coord, origin)`
248
+
249
+ Convert geographic coordinates to orthographic projection (hemisphere view).
250
+
251
+ ```typescript
252
+ function orthoProjection(
253
+ coord: GeoCoord,
254
+ origin: GeoCoord // Center of projection
255
+ ): {
256
+ coord: Point; // Projected point
257
+ clipped: boolean; // True if coord is on the back hemisphere
258
+ };
259
+ ```
260
+
261
+ **Example:**
262
+ ```typescript
263
+ // Project London as seen from the North Pole
264
+ const result = orthoProjection(
265
+ { latitude: 51.5074, longitude: -0.1278 },
266
+ { latitude: 90, longitude: 0 }
267
+ );
268
+
269
+ if (!result.clipped) {
270
+ console.log('Visible at:', result.coord);
271
+ }
272
+ ```
273
+
274
+ ### Type Definitions
275
+
276
+ ```typescript
277
+ type GeoCoord = {
278
+ latitude: number; // Degrees, -90 to 90
279
+ longitude: number; // Degrees, -180 to 180
280
+ };
281
+
282
+ type Point = {
283
+ x: number;
284
+ y: number;
285
+ };
286
+ ```
287
+
288
+ ## Common Use Cases
289
+
290
+ ### Calculate Flight Distance and Route
291
+
292
+ ```typescript
293
+ import {
294
+ greatCircleDistance,
295
+ initialBearingOfGreatCircle,
296
+ intermediatePointOnGreatCircle
297
+ } from '@ue-too/border';
298
+
299
+ const departure = { latitude: 40.6413, longitude: -73.7781 }; // JFK
300
+ const arrival = { latitude: 51.4700, longitude: -0.4543 }; // LHR
301
+
302
+ // Total distance
303
+ const distance = greatCircleDistance(departure, arrival);
304
+ console.log('Flight distance:', (distance / 1000).toFixed(0), 'km');
305
+
306
+ // Initial heading
307
+ const heading = initialBearingOfGreatCircle(departure, arrival);
308
+ console.log('Initial heading:', heading.toFixed(1), '°');
309
+
310
+ // Waypoint halfway through
311
+ const midpoint = intermediatePointOnGreatCircle(departure, arrival, 0.5);
312
+ console.log('Midpoint:', midpoint);
313
+ ```
314
+
315
+ ### Display Map with Mercator Projection
316
+
317
+ ```typescript
318
+ import { mercatorProjection } from '@ue-too/border';
319
+
320
+ const cities = [
321
+ { name: 'New York', latitude: 40.7128, longitude: -74.0060 },
322
+ { name: 'London', latitude: 51.5074, longitude: -0.1278 },
323
+ { name: 'Tokyo', latitude: 35.6762, longitude: 139.6503 }
324
+ ];
325
+
326
+ // Project to screen coordinates
327
+ cities.forEach(city => {
328
+ const point = mercatorProjection(city);
329
+
330
+ // Scale to canvas (assuming 1000x600 canvas)
331
+ const x = (point.x + 180) / 360 * 1000;
332
+ const y = (1 - (point.y + 90) / 180) * 600;
333
+
334
+ drawCity(x, y, city.name);
335
+ });
336
+ ```
337
+
338
+ ### Navigate with Constant Bearing
339
+
340
+ ```typescript
341
+ import {
342
+ rhumbBearing,
343
+ destinationFromOriginOnRhumbLine
344
+ } from '@ue-too/border';
345
+
346
+ const start = { latitude: 50.0, longitude: -5.0 };
347
+ const target = { latitude: 58.0, longitude: 3.0 };
348
+
349
+ // Get constant bearing to maintain
350
+ const bearing = rhumbBearing(start, target);
351
+ console.log('Sail on bearing:', bearing.toFixed(1), '°');
352
+
353
+ // Calculate positions every 50 nautical miles
354
+ const nauticalMileInMeters = 1852;
355
+ let currentPos = start;
356
+
357
+ for (let i = 1; i <= 10; i++) {
358
+ currentPos = destinationFromOriginOnRhumbLine(
359
+ currentPos,
360
+ bearing,
361
+ 50 * nauticalMileInMeters
362
+ );
363
+ console.log(`Waypoint ${i}:`, currentPos);
364
+ }
365
+ ```
366
+
367
+ ### Find Nearest City
368
+
369
+ ```typescript
370
+ import { greatCircleDistance } from '@ue-too/border';
371
+
372
+ const userLocation = { latitude: 48.8566, longitude: 2.3522 }; // Paris
373
+
374
+ const cities = [
375
+ { name: 'London', coord: { latitude: 51.5074, longitude: -0.1278 } },
376
+ { name: 'Berlin', coord: { latitude: 52.5200, longitude: 13.4050 } },
377
+ { name: 'Madrid', coord: { latitude: 40.4168, longitude: -3.7038 } },
378
+ { name: 'Rome', coord: { latitude: 41.9028, longitude: 12.4964 } }
379
+ ];
380
+
381
+ const distances = cities.map(city => ({
382
+ ...city,
383
+ distance: greatCircleDistance(userLocation, city.coord)
384
+ }));
385
+
386
+ distances.sort((a, b) => a.distance - b.distance);
387
+ console.log('Nearest city:', distances[0].name,
388
+ '- ', (distances[0].distance / 1000).toFixed(0), 'km');
389
+ ```
390
+
391
+ ### Globe Visualization with Orthographic Projection
392
+
393
+ ```typescript
394
+ import { orthoProjection } from '@ue-too/border';
395
+
396
+ // View centered on Europe
397
+ const viewCenter = { latitude: 50, longitude: 10 };
398
+
399
+ const cities = [
400
+ { name: 'London', coord: { latitude: 51.5074, longitude: -0.1278 } },
401
+ { name: 'Paris', coord: { latitude: 48.8566, longitude: 2.3522 } },
402
+ { name: 'Berlin', coord: { latitude: 52.5200, longitude: 13.4050 } }
403
+ ];
404
+
405
+ cities.forEach(city => {
406
+ const result = orthoProjection(city.coord, viewCenter);
407
+
408
+ if (!result.clipped) {
409
+ // City is visible on this hemisphere
410
+ const screenX = result.coord.x * 400 + 400; // Scale to canvas
411
+ const screenY = result.coord.y * 400 + 400;
412
+
413
+ drawCityOnGlobe(screenX, screenY, city.name);
414
+ }
415
+ });
416
+ ```
417
+
418
+ ## API Reference
419
+
420
+ For complete API documentation with detailed type information, see the [TypeDoc-generated documentation](../../docs/border).
421
+
422
+ ## TypeScript Support
423
+
424
+ This package is written in TypeScript with complete type definitions:
425
+
426
+ ```typescript
427
+ import { GeoCoord, Point, greatCircleDistance } from '@ue-too/border';
428
+
429
+ // Coordinates are fully typed
430
+ const coord: GeoCoord = { latitude: 51.5074, longitude: -0.1278 };
431
+
432
+ // Function signatures are type-safe
433
+ const distance: number = greatCircleDistance(coord1, coord2);
434
+
435
+ // Projection results are typed
436
+ const point: Point = mercatorProjection(coord);
437
+ ```
438
+
439
+ ## Design Philosophy
440
+
441
+ This library follows these principles:
442
+
443
+ - **Accuracy**: Uses proven geodesy algorithms from academic sources
444
+ - **Simplicity**: Clean, focused API for common geodesy tasks
445
+ - **Type Safety**: Full TypeScript type definitions
446
+ - **Performance**: Efficient calculations suitable for real-time applications
447
+ - **Practicality**: Focused on real-world mapping and navigation use cases
448
+
449
+ ## Performance Considerations
450
+
451
+ - **Distance calculations**: O(1) - simple trigonometric calculations
452
+ - **Projections**: O(1) - direct mathematical transformations
453
+ - **Intermediate points**: O(1) - no iteration required
454
+
455
+ **Performance Tips:**
456
+ - Cache distance and bearing calculations if coordinates don't change
457
+ - For many points, batch projection calculations
458
+ - Use appropriate projection for your use case (Mercator for general mapping, orthographic for globes)
459
+
460
+ ## Limitations
461
+
462
+ - **Earth model**: Assumes spherical Earth (not ellipsoidal) - sufficient for most applications but less accurate for high-precision surveying
463
+ - **Coordinate range**: Latitude must be in [-90, 90], longitude in [-180, 180]
464
+ - **Polar regions**: Some calculations may be less accurate near poles
465
+ - **Mercator distortion**: Mercator projection heavily distorts areas near poles
466
+
467
+ ## Related Packages
468
+
469
+ - **[@ue-too/math](../math)**: Vector operations for point calculations
470
+ - **[@ue-too/curve](../curve)**: Bezier curves for drawing map features
471
+ - **[@ue-too/board](../board)**: Canvas board for rendering maps
472
+
473
+ ## Further Reading
474
+
475
+ - [Movable Type Scripts](https://www.movable-type.co.uk/scripts/latlong.html) - Comprehensive geodesy reference
476
+ - [Map Projections](https://en.wikipedia.org/wiki/Map_projection) - Understanding different projections
477
+ - [Great Circle Navigation](https://en.wikipedia.org/wiki/Great-circle_navigation) - Theory and applications
478
+ - [Rhumb Line](https://en.wikipedia.org/wiki/Rhumb_line) - Constant bearing navigation
479
+
480
+ ## License
481
+
482
+ MIT
483
+
484
+ ## Repository
485
+
486
+ [https://github.com/ue-too/ue-too](https://github.com/ue-too/ue-too)
package/greateCircle.d.ts CHANGED
@@ -1,5 +1,54 @@
1
1
  import { GeoCoord } from "./projection";
2
+ /**
3
+ * Calculates an intermediate point along a great circle path.
4
+ *
5
+ * @remarks
6
+ * Given two points on Earth's surface, this finds a point at a specified
7
+ * fraction along the great circle (shortest) path between them.
8
+ *
9
+ * Uses the spherical interpolation formula for accurate results on a sphere.
10
+ *
11
+ * @param startCoord - The starting geographic coordinate
12
+ * @param endCoord - The ending geographic coordinate
13
+ * @param fraction - The fraction along the path (0 = start, 1 = end, 0.5 = midpoint)
14
+ * @returns The intermediate point at the specified fraction
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const nyc = { latitude: 40.7128, longitude: -74.0060 };
19
+ * const london = { latitude: 51.5074, longitude: -0.1278 };
20
+ *
21
+ * // Find point 25% of the way from NYC to London
22
+ * const quarter = intermediatePointOnGreatCircle(nyc, london, 0.25);
23
+ *
24
+ * // Find point 75% of the way
25
+ * const threeQuarters = intermediatePointOnGreatCircle(nyc, london, 0.75);
26
+ * ```
27
+ *
28
+ * @category Great Circle
29
+ */
2
30
  export declare function intermediatePointOnGreatCircle(startCoord: GeoCoord, endCoord: GeoCoord, fraction: number): GeoCoord;
31
+ /**
32
+ * Calculates the midpoint along a great circle path.
33
+ *
34
+ * @remarks
35
+ * This is a specialized, optimized version of {@link intermediatePointOnGreatCircle}
36
+ * for finding the exact midpoint (fraction = 0.5).
37
+ *
38
+ * @param startCoord - The starting geographic coordinate
39
+ * @param endCoord - The ending geographic coordinate
40
+ * @returns The midpoint on the great circle path
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const start = { latitude: 50.0, longitude: -5.0 };
45
+ * const end = { latitude: 58.0, longitude: 3.0 };
46
+ * const mid = midPointOnGreatCircle(start, end);
47
+ * console.log('Midpoint:', mid);
48
+ * ```
49
+ *
50
+ * @category Great Circle
51
+ */
3
52
  export declare function midPointOnGreatCircle(startCoord: GeoCoord, endCoord: GeoCoord): GeoCoord;
4
53
  /**
5
54
  * Calculate the initial bearing between two points on the Earth's surface.
@@ -10,5 +59,57 @@ export declare function midPointOnGreatCircle(startCoord: GeoCoord, endCoord: Ge
10
59
  * @returns The bearing in degrees.
11
60
  */
12
61
  export declare function initialBearingOfGreatCircle(startCoord: GeoCoord, endCoord: GeoCoord): number;
62
+ /**
63
+ * Calculates the great circle distance between two points on Earth.
64
+ *
65
+ * @remarks
66
+ * Uses the haversine formula to calculate the shortest distance over Earth's
67
+ * surface between two geographic coordinates. This is the "as-the-crow-flies"
68
+ * distance.
69
+ *
70
+ * The calculation assumes Earth's mean radius of 6,371,000 meters and treats
71
+ * Earth as a perfect sphere.
72
+ *
73
+ * @param startCoord - The starting geographic coordinate
74
+ * @param endCoord - The ending geographic coordinate
75
+ * @returns The distance in meters
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const nyc = { latitude: 40.7128, longitude: -74.0060 };
80
+ * const london = { latitude: 51.5074, longitude: -0.1278 };
81
+ *
82
+ * const distance = greatCircleDistance(nyc, london);
83
+ * console.log('Distance:', distance / 1000, 'km'); // ~5570 km
84
+ * ```
85
+ *
86
+ * @category Great Circle
87
+ */
13
88
  export declare function greatCircleDistance(startCoord: GeoCoord, endCoord: GeoCoord): number;
89
+ /**
90
+ * Calculates the destination point given a start point, bearing, and distance on a great circle.
91
+ *
92
+ * @remarks
93
+ * Starting from a given point and traveling along a great circle at a specific
94
+ * initial bearing for a given distance, this calculates where you'll end up.
95
+ *
96
+ * Note: The bearing will change along the path (except when traveling due north/south
97
+ * or along the equator) because great circles are not straight lines on most map projections.
98
+ *
99
+ * @param startCoord - The starting geographic coordinate
100
+ * @param bearing - The initial bearing in degrees (0 = north, 90 = east, etc.)
101
+ * @param distance - The distance to travel in meters
102
+ * @returns The destination coordinate
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const start = { latitude: 40.7128, longitude: -74.0060 }; // NYC
107
+ *
108
+ * // Travel 1000km northeast from NYC
109
+ * const destination = destinationFromOriginOnGreatCircle(start, 45, 1000000);
110
+ * console.log('Destination:', destination);
111
+ * ```
112
+ *
113
+ * @category Great Circle
114
+ */
14
115
  export declare function destinationFromOriginOnGreatCircle(startCoord: GeoCoord, bearing: number, distance: number): GeoCoord;
package/index.d.ts CHANGED
@@ -1,3 +1,120 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Geodesy and map projection library for TypeScript.
4
+ *
5
+ * @remarks
6
+ * The `@ue-too/border` package provides geodesy calculations and map projections
7
+ * for working with geographic coordinates on the Earth's surface. It includes utilities
8
+ * for great circle navigation, rhumb line navigation, and coordinate transformations.
9
+ *
10
+ * ## Core Concepts
11
+ *
12
+ * - **Great Circles**: Shortest path between two points on a sphere (like flight routes)
13
+ * - **Rhumb Lines**: Constant bearing paths (easier for navigation, slightly longer)
14
+ * - **Map Projections**: Converting geographic coordinates to 2D points and vice versa
15
+ *
16
+ * ## Key Features
17
+ *
18
+ * ### Great Circle Navigation
19
+ * - Calculate distance between two geographic points
20
+ * - Find initial bearing for great circle path
21
+ * - Calculate intermediate points along great circle
22
+ * - Find destination given origin, bearing, and distance
23
+ * - Calculate midpoint on great circle
24
+ *
25
+ * ### Rhumb Line Navigation
26
+ * - Calculate rhumb line distance (constant bearing path)
27
+ * - Find bearing for rhumb line
28
+ * - Calculate destination on rhumb line
29
+ * - Find midpoint on rhumb line
30
+ *
31
+ * ### Map Projections
32
+ * - **Mercator Projection**: Cylindrical map projection preserving angles
33
+ * - **Orthographic Projection**: Perspective view of hemisphere from space
34
+ * - Inverse projections for converting back to geographic coordinates
35
+ *
36
+ * ## Main Exports
37
+ *
38
+ * - {@link GeoCoord} - Geographic coordinate type (latitude/longitude)
39
+ * - {@link mercatorProjection} - Convert geo coordinates to Mercator projection
40
+ * - {@link orthoProjection} - Convert geo coordinates to orthographic projection
41
+ * - {@link greatCircleDistance} - Calculate great circle distance
42
+ * - {@link initialBearingOfGreatCircle} - Calculate initial bearing
43
+ * - {@link rhumbDistance} - Calculate rhumb line distance
44
+ * - {@link rhumbBearing} - Calculate rhumb line bearing
45
+ *
46
+ * @example
47
+ * Great circle navigation
48
+ * ```typescript
49
+ * import {
50
+ * greatCircleDistance,
51
+ * initialBearingOfGreatCircle,
52
+ * destinationFromOriginOnGreatCircle
53
+ * } from '@ue-too/border';
54
+ *
55
+ * // New York to London
56
+ * const nyc = { latitude: 40.7128, longitude: -74.0060 };
57
+ * const london = { latitude: 51.5074, longitude: -0.1278 };
58
+ *
59
+ * // Calculate distance in meters
60
+ * const distance = greatCircleDistance(nyc, london);
61
+ * console.log('Distance:', distance / 1000, 'km'); // ~5570 km
62
+ *
63
+ * // Calculate initial bearing
64
+ * const bearing = initialBearingOfGreatCircle(nyc, london);
65
+ * console.log('Bearing:', bearing, 'degrees'); // ~51 degrees (northeast)
66
+ *
67
+ * // Find point 1000km along the path
68
+ * const intermediate = destinationFromOriginOnGreatCircle(nyc, bearing, 1000000);
69
+ * ```
70
+ *
71
+ * @example
72
+ * Rhumb line navigation
73
+ * ```typescript
74
+ * import { rhumbDistance, rhumbBearing, destinationFromOriginOnRhumbLine } from '@ue-too/border';
75
+ *
76
+ * const start = { latitude: 50.0, longitude: -5.0 };
77
+ * const end = { latitude: 58.0, longitude: 3.0 };
78
+ *
79
+ * // Calculate rhumb line distance
80
+ * const dist = rhumbDistance(start, end);
81
+ *
82
+ * // Calculate constant bearing
83
+ * const bearing = rhumbBearing(start, end);
84
+ *
85
+ * // Navigate 100km on constant bearing
86
+ * const destination = destinationFromOriginOnRhumbLine(start, bearing, 100000);
87
+ * ```
88
+ *
89
+ * @example
90
+ * Map projections
91
+ * ```typescript
92
+ * import {
93
+ * mercatorProjection,
94
+ * inverseMercatorProjection,
95
+ * orthoProjection
96
+ * } from '@ue-too/border';
97
+ *
98
+ * // Convert geographic to Mercator
99
+ * const coord = { latitude: 51.5074, longitude: -0.1278 };
100
+ * const point = mercatorProjection(coord);
101
+ * console.log('Mercator point:', point); // {x: ..., y: ...}
102
+ *
103
+ * // Convert back to geographic
104
+ * const geoCoord = inverseMercatorProjection(point);
105
+ *
106
+ * // Orthographic projection (hemisphere view)
107
+ * const origin = { latitude: 45.0, longitude: 0.0 };
108
+ * const result = orthoProjection(coord, origin);
109
+ * if (!result.clipped) {
110
+ * console.log('Visible at:', result.coord);
111
+ * }
112
+ * ```
113
+ *
114
+ * @see {@link projection} for map projection functions
115
+ * @see {@link greateCircle} for great circle navigation
116
+ * @see {@link rhumbLine} for rhumb line navigation
117
+ */
1
118
  export * from "./projection";
2
119
  export * from "./greateCircle";
3
120
  export * from "./rhumbLine";
package/index.js CHANGED
@@ -1,173 +1,3 @@
1
- // src/projection.ts
2
- function mercatorProjection(interestPoint, centerLongitude = 0) {
3
- const r = 6371000;
4
- const latitude = interestPoint.latitude * Math.PI / 180;
5
- const longitude = interestPoint.longitude * Math.PI / 180;
6
- let x = r * normalizeAngleMinusPiToPi(longitude - centerLongitude * Math.PI / 180);
7
- let y = r * Math.log(Math.tan(Math.PI / 4 + latitude / 2));
8
- return { x, y };
9
- }
10
- function inverseMercatorProjection(point, centerLongitude = 0) {
11
- const r = 6371000;
12
- const longitude = point.x / r + centerLongitude;
13
- const latitude = 2 * Math.atan(Math.exp(point.y / r)) - Math.PI / 2;
14
- return { latitude: latitude * 180 / Math.PI, longitude: longitude * 180 / Math.PI };
15
- }
16
- function orthoProjection(interestPoint, origin) {
17
- const r = 6371000;
18
- const latitude = interestPoint.latitude * Math.PI / 180;
19
- const longitude = interestPoint.longitude * Math.PI / 180;
20
- const x = r * Math.cos(latitude) * Math.sin(longitude - origin.longitude * Math.PI / 180);
21
- const y = r * (Math.cos(origin.latitude * Math.PI / 180) * Math.sin(latitude) - Math.sin(origin.latitude * Math.PI / 180) * Math.cos(latitude) * Math.cos(longitude - origin.longitude * Math.PI / 180));
22
- const clipped = Math.sin(origin.latitude * Math.PI / 180) * Math.sin(latitude) + Math.cos(origin.latitude * Math.PI / 180) * Math.cos(latitude) * Math.cos(longitude - origin.longitude * Math.PI / 180);
23
- return { clipped: clipped < 0, coord: { x, y } };
24
- }
25
- function inverseOrthoProjection(interestPoint, origin) {
26
- const r = 6371000;
27
- const rho = Math.sqrt(interestPoint.x * interestPoint.x + interestPoint.y * interestPoint.y);
28
- const c = Math.asin(rho / r);
29
- const latitude = Math.asin(Math.cos(c) * Math.sin(origin.latitude * Math.PI / 180) + interestPoint.y * Math.sin(c) * Math.cos(origin.latitude * Math.PI / 180) / rho) * 180 / Math.PI;
30
- const longitude = origin.longitude + Math.atan2(interestPoint.x * Math.sin(c), rho * Math.cos(c) * Math.cos(origin.latitude * Math.PI / 180) - interestPoint.y * Math.sin(c) * Math.sin(origin.latitude * Math.PI / 180)) * 180 / Math.PI;
31
- return { latitude, longitude };
32
- }
33
- function normalizeAngleMinusPiToPi(angle) {
34
- angle = angle % (Math.PI * 2);
35
- angle = (angle + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
36
- return angle;
37
- }
38
- // src/greateCircle.ts
39
- function intermediatePointOnGreatCircle(startCoord, endCoord, fraction) {
40
- const Δ_ = (endCoord.latitude - startCoord.latitude) * Math.PI / 180;
41
- const Δ_2 = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;
42
- const φ1 = startCoord.latitude * Math.PI / 180;
43
- const λ1 = startCoord.longitude * Math.PI / 180;
44
- const φ2 = endCoord.latitude * Math.PI / 180;
45
- const λ2 = endCoord.longitude * Math.PI / 180;
46
- const angularDistA = Math.sin(Δ_ / 2) * Math.sin(Δ_ / 2) + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δ_2 / 2) * Math.sin(Δ_2 / 2);
47
- const c = 2 * Math.atan2(Math.sqrt(angularDistA), Math.sqrt(1 - angularDistA));
48
- const a = Math.sin((1 - fraction) * c) / Math.sin(c);
49
- const b = Math.sin(fraction * c) / Math.sin(c);
50
- const x = a * Math.cos(φ1) * Math.cos(λ1) + b * Math.cos(φ2) * Math.cos(λ2);
51
- const y = a * Math.cos(φ1) * Math.sin(λ1) + b * Math.cos(φ2) * Math.sin(λ2);
52
- const z = a * Math.sin(φ1) * b * Math.sin(φ2);
53
- const φi = Math.atan2(z, Math.sqrt(x * x + y * y));
54
- const λi = Math.atan2(y, x);
55
- return { latitude: φi, longitude: λi };
56
- }
57
- function midPointOnGreatCircle(startCoord, endCoord) {
58
- const φ1 = startCoord.latitude * Math.PI / 180;
59
- const φ2 = endCoord.latitude * Math.PI / 180;
60
- const λ1 = startCoord.longitude * Math.PI / 180;
61
- const λ2 = endCoord.longitude * Math.PI / 180;
62
- const Bx = Math.cos(φ2) * Math.cos(λ2 - λ1);
63
- const By = Math.cos(φ2) * Math.sin(λ2 - λ1);
64
- const φ3 = Math.atan2(Math.sin(φ1) + Math.sin(φ2), Math.sqrt((Math.cos(φ1) + Bx) * (Math.cos(φ1) + Bx) + By * By));
65
- const λ3 = λ1 + Math.atan2(By, Math.cos(φ1) + Bx);
66
- return { latitude: φ3, longitude: λ3 };
67
- }
68
- function initialBearingOfGreatCircle(startCoord, endCoord) {
69
- const φ1 = startCoord.latitude * Math.PI / 180;
70
- const φ2 = endCoord.latitude * Math.PI / 180;
71
- const λ1 = startCoord.longitude * Math.PI / 180;
72
- const λ2 = endCoord.longitude * Math.PI / 180;
73
- const y = Math.sin(λ2 - λ1) * Math.cos(φ2);
74
- const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(λ2 - λ1);
75
- const θ = Math.atan2(y, x);
76
- const brng = (θ * 180 / Math.PI + 360) % 360;
77
- return brng;
78
- }
79
- function greatCircleDistance(startCoord, endCoord) {
80
- const R = 6371000;
81
- const φ1 = startCoord.latitude * Math.PI / 180;
82
- const φ2 = endCoord.latitude * Math.PI / 180;
83
- const Δ_ = (endCoord.latitude - startCoord.latitude) * Math.PI / 180;
84
- const Δ_2 = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;
85
- const a = Math.sin(Δ_ / 2) * Math.sin(Δ_ / 2) + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δ_2 / 2) * Math.sin(Δ_2 / 2);
86
- const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
87
- const d = R * c;
88
- return d;
89
- }
90
- function destinationFromOriginOnGreatCircle(startCoord, bearing, distance) {
91
- const R = 6371000;
92
- const φ1 = startCoord.latitude * Math.PI / 180;
93
- const λ1 = startCoord.longitude * Math.PI / 180;
94
- const θ = bearing * Math.PI / 180;
95
- const d = distance / R;
96
- const φ2 = Math.asin(Math.sin(φ1) * Math.cos(d) + Math.cos(φ1) * Math.sin(d) * Math.cos(θ));
97
- const λ2 = λ1 + Math.atan2(Math.sin(θ) * Math.sin(d) * Math.cos(φ1), Math.cos(d) - Math.sin(φ1) * Math.sin(φ2));
98
- return { latitude: φ2, longitude: λ2 };
99
- }
100
- // src/rhumbLine.ts
101
- function rhumbDistance(startCoord, endCoord) {
102
- const R = 6371000;
103
- const φ1 = startCoord.latitude * Math.PI / 180;
104
- const φ2 = endCoord.latitude * Math.PI / 180;
105
- const Δ_ = (endCoord.latitude - startCoord.latitude) * Math.PI / 180;
106
- let Δ_2 = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;
107
- const Δ_3 = Math.log(Math.tan(Math.PI / 4 + φ2 / 2) / Math.tan(Math.PI / 4 + φ1 / 2));
108
- const q = Math.abs(Δ_3) > 0.00000000001 ? Δ_ / Δ_3 : Math.cos(φ1);
109
- if (Math.abs(Δ_2) > Math.PI)
110
- Δ_2 = Δ_2 > 0 ? -(2 * Math.PI - Δ_2) : 2 * Math.PI + Δ_2;
111
- const dist = Math.sqrt(Δ_ * Δ_ + q * q * Δ_2 * Δ_2) * R;
112
- return dist;
113
- }
114
- function rhumbBearing(startCoord, endCoord) {
115
- const φ1 = startCoord.latitude * Math.PI / 180;
116
- const φ2 = endCoord.latitude * Math.PI / 180;
117
- let Δ_ = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;
118
- const Δ_2 = Math.log(Math.tan(Math.PI / 4 + φ2 / 2) / Math.tan(Math.PI / 4 + φ1 / 2));
119
- if (Math.abs(Δ_) > Math.PI)
120
- Δ_ = Δ_ > 0 ? -(2 * Math.PI - Δ_) : 2 * Math.PI + Δ_;
121
- const brng = Math.atan2(Δ_, Δ_2) * 180 / Math.PI;
122
- return brng;
123
- }
124
- function destinationFromOriginOnRhumbLine(startCoord, bearing, distance) {
125
- const R = 6371000;
126
- const φ1 = startCoord.latitude * Math.PI / 180;
127
- const λ1 = startCoord.longitude * Math.PI / 180;
128
- const θ = bearing * Math.PI / 180;
129
- const d = distance;
130
- const δ = d / R;
131
- const Δ_ = δ * Math.cos(θ);
132
- let φ2 = φ1 + Δ_;
133
- const Δ_2 = Math.log(Math.tan(φ2 / 2 + Math.PI / 4) / Math.tan(φ1 / 2 + Math.PI / 4));
134
- const q = Math.abs(Δ_2) > 0.00000000001 ? Δ_ / Δ_2 : Math.cos(φ1);
135
- const Δ_3 = δ * Math.sin(θ) / q;
136
- const λ2 = λ1 + Δ_3;
137
- if (Math.abs(φ2) > Math.PI / 2)
138
- φ2 = φ2 > 0 ? Math.PI - φ2 : -Math.PI - φ2;
139
- return { latitude: φ2, longitude: λ2 };
140
- }
141
- function midPointOnRhumbLine(startCoord, endCoord) {
142
- let λ1 = startCoord.longitude * Math.PI / 180;
143
- const λ2 = endCoord.longitude * Math.PI / 180;
144
- const φ1 = startCoord.latitude * Math.PI / 180;
145
- const φ2 = endCoord.latitude * Math.PI / 180;
146
- if (Math.abs(λ2 - λ1) > Math.PI)
147
- λ1 += 2 * Math.PI;
148
- const φ3 = (φ1 + φ2) / 2;
149
- const f1 = Math.tan(Math.PI / 4 + φ1 / 2);
150
- const f2 = Math.tan(Math.PI / 4 + φ2 / 2);
151
- const f3 = Math.tan(Math.PI / 4 + φ3 / 2);
152
- let λ3 = ((λ2 - λ1) * Math.log(f3) + λ1 * Math.log(f2) - λ2 * Math.log(f1)) / Math.log(f2 / f1);
153
- if (!isFinite(λ3))
154
- λ3 = (λ1 + λ2) / 2;
155
- return { latitude: φ3, longitude: λ3 };
156
- }
157
- export {
158
- rhumbDistance,
159
- rhumbBearing,
160
- orthoProjection,
161
- midPointOnRhumbLine,
162
- midPointOnGreatCircle,
163
- mercatorProjection,
164
- inverseOrthoProjection,
165
- inverseMercatorProjection,
166
- intermediatePointOnGreatCircle,
167
- initialBearingOfGreatCircle,
168
- greatCircleDistance,
169
- destinationFromOriginOnRhumbLine,
170
- destinationFromOriginOnGreatCircle
171
- };
1
+ function w(c,e=0){let p=c.latitude*Math.PI/180,l=c.longitude*Math.PI/180,m=6371000*D(l-e*Math.PI/180),i=6371000*Math.log(Math.tan(Math.PI/4+p/2));return{x:m,y:i}}function R(c,e=0){let p=c.x/6371000+e;return{latitude:(2*Math.atan(Math.exp(c.y/6371000))-Math.PI/2)*180/Math.PI,longitude:p*180/Math.PI}}function g(c,e){let p=c.latitude*Math.PI/180,l=c.longitude*Math.PI/180,m=6371000*Math.cos(p)*Math.sin(l-e.longitude*Math.PI/180),i=6371000*(Math.cos(e.latitude*Math.PI/180)*Math.sin(p)-Math.sin(e.latitude*Math.PI/180)*Math.cos(p)*Math.cos(l-e.longitude*Math.PI/180));return{clipped:Math.sin(e.latitude*Math.PI/180)*Math.sin(p)+Math.cos(e.latitude*Math.PI/180)*Math.cos(p)*Math.cos(l-e.longitude*Math.PI/180)<0,coord:{x:m,y:i}}}function E(c,e){let p=Math.sqrt(c.x*c.x+c.y*c.y),l=Math.asin(p/6371000),m=Math.asin(Math.cos(l)*Math.sin(e.latitude*Math.PI/180)+c.y*Math.sin(l)*Math.cos(e.latitude*Math.PI/180)/p)*180/Math.PI,i=e.longitude+Math.atan2(c.x*Math.sin(l),p*Math.cos(l)*Math.cos(e.latitude*Math.PI/180)-c.y*Math.sin(l)*Math.sin(e.latitude*Math.PI/180))*180/Math.PI;return{latitude:m,longitude:i}}function D(c){return c=c%(Math.PI*2),c=(c+3*Math.PI)%(2*Math.PI)-Math.PI,c}function B(c,e,b){let p=(e.latitude-c.latitude)*Math.PI/180,l=(e.longitude-c.longitude)*Math.PI/180,m=c.latitude*Math.PI/180,i=c.longitude*Math.PI/180,v=e.latitude*Math.PI/180,j=e.longitude*Math.PI/180,u=Math.sin(p/2)*Math.sin(p/2)+Math.cos(m)*Math.cos(v)*Math.sin(l/2)*Math.sin(l/2),k=2*Math.atan2(Math.sqrt(u),Math.sqrt(1-u)),h=Math.sin((1-b)*k)/Math.sin(k),f=Math.sin(b*k)/Math.sin(k),a=h*Math.cos(m)*Math.cos(i)+f*Math.cos(v)*Math.cos(j),O=h*Math.cos(m)*Math.sin(i)+f*Math.cos(v)*Math.sin(j),G=h*Math.sin(m)*f*Math.sin(v),M=Math.atan2(G,Math.sqrt(a*a+O*O)),F=Math.atan2(O,a);return{latitude:M,longitude:F}}function K(c,e){let b=c.latitude*Math.PI/180,p=e.latitude*Math.PI/180,l=c.longitude*Math.PI/180,m=e.longitude*Math.PI/180,i=Math.cos(p)*Math.cos(m-l),v=Math.cos(p)*Math.sin(m-l),j=Math.atan2(Math.sin(b)+Math.sin(p),Math.sqrt((Math.cos(b)+i)*(Math.cos(b)+i)+v*v)),u=l+Math.atan2(v,Math.cos(b)+i);return{latitude:j,longitude:u}}function N(c,e){let b=c.latitude*Math.PI/180,p=e.latitude*Math.PI/180,l=c.longitude*Math.PI/180,m=e.longitude*Math.PI/180,i=Math.sin(m-l)*Math.cos(p),v=Math.cos(b)*Math.sin(p)-Math.sin(b)*Math.cos(p)*Math.cos(m-l);return(Math.atan2(i,v)*180/Math.PI+360)%360}function S(c,e){let p=c.latitude*Math.PI/180,l=e.latitude*Math.PI/180,m=(e.latitude-c.latitude)*Math.PI/180,i=(e.longitude-c.longitude)*Math.PI/180,v=Math.sin(m/2)*Math.sin(m/2)+Math.cos(p)*Math.cos(l)*Math.sin(i/2)*Math.sin(i/2);return 6371000*(2*Math.atan2(Math.sqrt(v),Math.sqrt(1-v)))}function V(c,e,b){let l=c.latitude*Math.PI/180,m=c.longitude*Math.PI/180,i=e*Math.PI/180,v=b/6371000,j=Math.asin(Math.sin(l)*Math.cos(v)+Math.cos(l)*Math.sin(v)*Math.cos(i)),u=m+Math.atan2(Math.sin(i)*Math.sin(v)*Math.cos(l),Math.cos(v)-Math.sin(l)*Math.sin(j));return{latitude:j,longitude:u}}function H(c,e){let p=c.latitude*Math.PI/180,l=e.latitude*Math.PI/180,m=(e.latitude-c.latitude)*Math.PI/180,i=(e.longitude-c.longitude)*Math.PI/180,v=Math.log(Math.tan(Math.PI/4+l/2)/Math.tan(Math.PI/4+p/2)),j=Math.abs(v)>0.00000000001?m/v:Math.cos(p);if(Math.abs(i)>Math.PI)i=i>0?-(2*Math.PI-i):2*Math.PI+i;return Math.sqrt(m*m+j*j*i*i)*6371000}function J(c,e){let b=c.latitude*Math.PI/180,p=e.latitude*Math.PI/180,l=(e.longitude-c.longitude)*Math.PI/180,m=Math.log(Math.tan(Math.PI/4+p/2)/Math.tan(Math.PI/4+b/2));if(Math.abs(l)>Math.PI)l=l>0?-(2*Math.PI-l):2*Math.PI+l;return Math.atan2(l,m)*180/Math.PI}function L(c,e,b){let l=c.latitude*Math.PI/180,m=c.longitude*Math.PI/180,i=e*Math.PI/180,j=b/6371000,u=j*Math.cos(i),k=l+u,h=Math.log(Math.tan(k/2+Math.PI/4)/Math.tan(l/2+Math.PI/4)),f=Math.abs(h)>0.00000000001?u/h:Math.cos(l),a=j*Math.sin(i)/f,O=m+a;if(Math.abs(k)>Math.PI/2)k=k>0?Math.PI-k:-Math.PI-k;return{latitude:k,longitude:O}}function Q(c,e){let b=c.longitude*Math.PI/180,p=e.longitude*Math.PI/180,l=c.latitude*Math.PI/180,m=e.latitude*Math.PI/180;if(Math.abs(p-b)>Math.PI)b+=2*Math.PI;let i=(l+m)/2,v=Math.tan(Math.PI/4+l/2),j=Math.tan(Math.PI/4+m/2),u=Math.tan(Math.PI/4+i/2),k=((p-b)*Math.log(u)+b*Math.log(j)-p*Math.log(v))/Math.log(j/v);if(!isFinite(k))k=(b+p)/2;return{latitude:i,longitude:k}}export{H as rhumbDistance,J as rhumbBearing,g as orthoProjection,Q as midPointOnRhumbLine,K as midPointOnGreatCircle,w as mercatorProjection,E as inverseOrthoProjection,R as inverseMercatorProjection,B as intermediatePointOnGreatCircle,N as initialBearingOfGreatCircle,S as greatCircleDistance,L as destinationFromOriginOnRhumbLine,V as destinationFromOriginOnGreatCircle};
172
2
 
173
- //# debugId=7FC1B3305866843664756E2164756E21
3
+ //# debugId=ADDDACDC1E32424A64756E2164756E21
package/index.js.map CHANGED
@@ -2,11 +2,11 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/projection.ts", "../src/greateCircle.ts", "../src/rhumbLine.ts"],
4
4
  "sourcesContent": [
5
- "import { Point } from \"@ue-too/math\";\n\nexport type GeoCoord = {\n longitude: number;\n latitude: number;\n}\n\nexport function mercatorProjection(interestPoint: GeoCoord, centerLongitude: number = 0): Point{\n const r = 6371000;\n const latitude = interestPoint.latitude * Math.PI / 180;\n const longitude = interestPoint.longitude * Math.PI / 180;\n let x = r * normalizeAngleMinusPiToPi(longitude - centerLongitude * Math.PI / 180);\n let y = r * Math.log(Math.tan(Math.PI / 4 + latitude / 2));\n return {x: x, y: y};\n}\n\nexport function inverseMercatorProjection(point: Point, centerLongitude: number = 0): GeoCoord{\n const r = 6371000;\n const longitude = point.x / r + centerLongitude;\n const latitude = 2 * Math.atan(Math.exp(point.y / r)) - Math.PI / 2;\n return {latitude: latitude * 180 / Math.PI, longitude: longitude * 180 / Math.PI};\n}\n\nexport function orthoProjection(interestPoint: GeoCoord, origin: GeoCoord): {clipped: boolean, coord: Point}{\n const r = 6371000;\n const latitude = interestPoint.latitude * Math.PI / 180;\n const longitude = interestPoint.longitude * Math.PI / 180;\n const x = r * Math.cos(latitude) * Math.sin(longitude - origin.longitude * Math.PI / 180);\n const y = r * (Math.cos(origin.latitude * Math.PI / 180) * Math.sin(latitude) - Math.sin(origin.latitude * Math.PI / 180) * Math.cos(latitude) * Math.cos(longitude - origin.longitude * Math.PI / 180));\n const clipped = Math.sin(origin.latitude * Math.PI / 180) * Math.sin(latitude) + Math.cos(origin.latitude * Math.PI / 180) * Math.cos(latitude) * Math.cos(longitude - origin.longitude * Math.PI / 180);\n\n return {clipped: clipped < 0, coord: {x: x, y: y}};\n}\n\nexport function inverseOrthoProjection(interestPoint: Point, origin: GeoCoord): GeoCoord {\n const r = 6371000;\n const rho = Math.sqrt(interestPoint.x * interestPoint.x + interestPoint.y * interestPoint.y);\n const c = Math.asin(rho / r);\n const latitude = Math.asin(Math.cos(c) * Math.sin(origin.latitude * Math.PI / 180) + (interestPoint.y * Math.sin(c) * Math.cos(origin.latitude * Math.PI / 180)) / rho) * 180 / Math.PI;\n const longitude = origin.longitude + Math.atan2(interestPoint.x * Math.sin(c), rho * Math.cos(c)*Math.cos(origin.latitude * Math.PI / 180) - interestPoint.y * Math.sin(c) * Math.sin(origin.latitude * Math.PI / 180)) * 180 / Math.PI;\n return {latitude: latitude, longitude: longitude};\n}\n\nfunction normalizeAngleMinusPiToPi(angle: number): number {\n // Reduce the angle\n angle = angle % (Math.PI * 2);\n\n // Force it to be in the range -π to π\n angle = (angle + 3 * Math.PI) % (2 * Math.PI) - Math.PI;\n\n return angle;\n}\n",
6
- "import { GeoCoord } from \"./projection\";\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy representation conversion functions (c) Chris Veness 2002-2022 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong.html */\n/* www.movable-type.co.uk/scripts/js/geodesy/geodesy-library.html#dms */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\nexport function intermediatePointOnGreatCircle(startCoord: GeoCoord, endCoord: GeoCoord, fraction: number): GeoCoord{\n const Δφ = (endCoord.latitude - startCoord.latitude) * Math.PI / 180;\n const Δλ = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;\n const φ1 = startCoord.latitude * Math.PI / 180;\n const λ1 = startCoord.longitude * Math.PI / 180;\n const φ2 = endCoord.latitude * Math.PI / 180;\n const λ2 = endCoord.longitude * Math.PI / 180;\n const angularDistA = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +\n Math.cos(φ1) * Math.cos(φ2) *\n Math.sin(Δλ / 2) * Math.sin(Δλ / 2);\n const c = 2 * Math.atan2(Math.sqrt(angularDistA), Math.sqrt(1 - angularDistA));\n \n const a = Math.sin((1 - fraction) * c) / Math.sin(c);\n const b = Math.sin(fraction * c) / Math.sin(c);\n const x = a * Math.cos(φ1) * Math.cos(λ1) + b * Math.cos(φ2) * Math.cos(λ2);\n const y = a * Math.cos(φ1) * Math.sin(λ1) + b * Math.cos(φ2) * Math.sin(λ2);\n const z = a * Math.sin(φ1) * b * Math.sin(φ2);\n const φi = Math.atan2(z, Math.sqrt(x * x + y * y));\n const λi = Math.atan2(y, x);\n return {latitude: φi, longitude: λi};\n}\n\nexport function midPointOnGreatCircle(startCoord: GeoCoord, endCoord: GeoCoord): GeoCoord{\n const φ1 = startCoord.latitude * Math.PI / 180;\n const φ2 = endCoord.latitude * Math.PI / 180;\n const λ1 = startCoord.longitude * Math.PI / 180;\n const λ2 = endCoord.longitude * Math.PI / 180;\n const Bx = Math.cos(φ2) * Math.cos(λ2 - λ1);\n const By = Math.cos(φ2) * Math.sin(λ2 - λ1);\n const φ3 = Math.atan2(Math.sin(φ1) + Math.sin(φ2),\n Math.sqrt( (Math.cos(φ1) + Bx) * (Math.cos(φ1) + Bx) + By * By ) );\n const λ3 = λ1 + Math.atan2(By, Math.cos(φ1) + Bx);\n return {latitude: φ3, longitude: λ3};\n}\n\n/**\n * Calculate the initial bearing between two points on the Earth's surface. \n * (traveling along the great circle would result in different bearing from the start point to the end point)\n * \n * @param startCoord - The starting point in GeoCoord format.\n * @param endCoord - The ending point in GeoCoord format.\n * @returns The bearing in degrees.\n */\nexport function initialBearingOfGreatCircle(startCoord: GeoCoord, endCoord: GeoCoord): number{\n const φ1 = startCoord.latitude * Math.PI / 180;\n const φ2 = endCoord.latitude * Math.PI / 180;\n const λ1 = startCoord.longitude * Math.PI / 180;\n const λ2 = endCoord.longitude * Math.PI / 180;\n const y = Math.sin(λ2-λ1) * Math.cos(φ2);\n const x = Math.cos(φ1) * Math.sin(φ2) -\n Math.sin(φ1) * Math.cos(φ2)*Math.cos(λ2 - λ1);\n const θ = Math.atan2(y, x);\n const brng = (θ * 180 / Math.PI + 360) % 360; // in degrees\n return brng;\n}\n\nexport function greatCircleDistance(startCoord: GeoCoord, endCoord: GeoCoord): number{\n const R = 6371e3; // metres\n const φ1 = startCoord.latitude * Math.PI / 180; // φ, λ in radians\n const φ2 = endCoord.latitude * Math.PI / 180;\n const Δφ = (endCoord.latitude - startCoord.latitude) * Math.PI / 180;\n const Δλ = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;\n\n const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +\n Math.cos(φ1) * Math.cos(φ2) *\n Math.sin(Δλ / 2) * Math.sin(Δλ / 2);\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n const d = R * c; // in metres\n return d;\n} \n\nexport function destinationFromOriginOnGreatCircle(startCoord: GeoCoord, bearing: number, distance: number): GeoCoord{\n const R = 6371e3; // metres\n const φ1 = startCoord.latitude * Math.PI / 180; // φ, λ in radians\n const λ1 = startCoord.longitude * Math.PI / 180;\n const θ = bearing * Math.PI / 180;\n const d = distance / R; // angular distance in radians\n const φ2 = Math.asin(Math.sin(φ1) * Math.cos(d) +\n Math.cos(φ1) * Math.sin(d) * Math.cos(θ) );\n const λ2 = λ1 + Math.atan2(Math.sin(θ) * Math.sin(d) * Math.cos(φ1),\n Math.cos(d) - Math.sin(φ1) * Math.sin(φ2));\n return {latitude: φ2, longitude: λ2};\n}\n",
7
- "import { GeoCoord } from \"./projection\";\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy representation conversion functions (c) Chris Veness 2002-2022 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong.html */\n/* www.movable-type.co.uk/scripts/js/geodesy/geodesy-library.html#dms */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport function rhumbDistance(startCoord: GeoCoord, endCoord: GeoCoord): number{\n const R = 6371e3; // metres\n const φ1 = startCoord.latitude * Math.PI/180; // φ, λ in radians\n const φ2 = endCoord.latitude * Math.PI/180;\n const Δφ = (endCoord.latitude - startCoord.latitude) * Math.PI/180;\n let Δλ = (endCoord.longitude - startCoord.longitude) * Math.PI/180;\n const Δψ = Math.log(Math.tan(Math.PI / 4 + φ2 / 2)/Math.tan(Math.PI / 4 + φ1 / 2));\n const q = Math.abs(Δψ) > 10e-12 ? Δφ / Δψ : Math.cos(φ1); // E-W course becomes ill-conditioned with 0/0\n\n // if dLon over 180° take shorter rhumb line across the anti-meridian:\n if (Math.abs(Δλ) > Math.PI) Δλ = Δλ > 0 ? - (2 * Math.PI - Δλ) : (2 * Math.PI + Δλ);\n\n const dist = Math.sqrt(Δφ * Δφ + q * q * Δλ * Δλ) * R;\n return dist;\n}\n\nexport function rhumbBearing(startCoord: GeoCoord, endCoord: GeoCoord): number{\n const φ1 = startCoord.latitude * Math.PI / 180; // φ, λ in radians\n const φ2 = endCoord.latitude * Math.PI / 180;\n let Δλ = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;\n const Δψ = Math.log(Math.tan(Math.PI / 4 + φ2 / 2) / Math.tan(Math.PI / 4 + φ1 / 2));\n\n // if dLon over 180° take shorter rhumb line across the anti-meridian:\n if (Math.abs(Δλ) > Math.PI) Δλ = Δλ > 0 ? -(2 * Math.PI-Δλ) : (2 * Math.PI + Δλ);\n\n const brng = Math.atan2(Δλ, Δψ) * 180 / Math.PI;\n return brng;\n}\n\nexport function destinationFromOriginOnRhumbLine(startCoord: GeoCoord, bearing: number, distance: number): GeoCoord{\n const R = 6371e3; // metres\n const φ1 = startCoord.latitude * Math.PI / 180; // φ, λ in radians\n const λ1 = startCoord.longitude * Math.PI / 180;\n const θ = bearing * Math.PI / 180;\n const d = distance;\n const δ = d / R;\n const Δφ = δ * Math.cos(θ);\n let φ2 = φ1 + Δφ;\n\n const Δψ = Math.log(Math.tan(φ2 / 2 + Math.PI / 4) / Math.tan(φ1 / 2 + Math.PI / 4));\n const q = Math.abs(Δψ) > 10e-12 ? Δφ / Δψ : Math.cos(φ1); // E-W course becomes ill-conditioned with 0/0\n\n const Δλ = δ * Math.sin(θ) / q;\n const λ2 = λ1 + Δλ;\n\n // check for some daft bugger going past the pole, normalise latitude if so\n if (Math.abs(φ2) > Math.PI / 2) φ2 = φ2 > 0 ? Math.PI - φ2 : -Math.PI - φ2;\n return {latitude: φ2, longitude: λ2};\n}\n\nexport function midPointOnRhumbLine(startCoord: GeoCoord, endCoord: GeoCoord): GeoCoord{\n let λ1 = startCoord.longitude * Math.PI / 180; \n const λ2 = endCoord.longitude * Math.PI / 180;\n const φ1 = startCoord.latitude * Math.PI / 180;\n const φ2 = endCoord.latitude * Math.PI / 180; \n if (Math.abs(λ2 - λ1) > Math.PI) λ1 += 2 * Math.PI; // crossing anti-meridian\n\n const φ3 = (φ1 + φ2) / 2;\n const f1 = Math.tan(Math.PI / 4 + φ1 / 2);\n const f2 = Math.tan(Math.PI / 4 + φ2 / 2);\n const f3 = Math.tan(Math.PI / 4 + φ3 / 2);\n let λ3 = ( (λ2 - λ1) * Math.log(f3) + λ1 * Math.log(f2) - λ2 * Math.log(f1) ) / Math.log(f2 / f1);\n\n if (!isFinite(λ3)) λ3 = (λ1 + λ2) / 2; // parallel of latitude\n return {latitude: φ3, longitude: λ3};\n}\n"
5
+ "import { Point } from \"@ue-too/math\";\n\n/**\n * Geographic coordinate representing a location on Earth's surface.\n *\n * @remarks\n * Coordinates use the WGS84 standard:\n * - Latitude: -90 to 90 degrees (negative = south, positive = north)\n * - Longitude: -180 to 180 degrees (negative = west, positive = east)\n *\n * @category Types\n */\nexport type GeoCoord = {\n /** Longitude in degrees (-180 to 180) */\n longitude: number;\n /** Latitude in degrees (-90 to 90) */\n latitude: number;\n}\n\n/**\n * Projects a geographic coordinate to Mercator projection.\n *\n * @remarks\n * The Mercator projection is a cylindrical map projection that preserves angles\n * and shapes locally. It's widely used for navigation because straight lines on\n * the map represent constant bearings (rhumb lines).\n *\n * The projection uses Earth's mean radius of 6,371,000 meters.\n *\n * Note: The Mercator projection becomes increasingly distorted near the poles.\n *\n * @param interestPoint - The geographic coordinate to project\n * @param centerLongitude - The central meridian in degrees (default: 0)\n * @returns The projected point in meters from the central meridian\n *\n * @example\n * ```typescript\n * const coord = { latitude: 51.5074, longitude: -0.1278 }; // London\n * const point = mercatorProjection(coord);\n * console.log(point); // { x: -14232.4, y: 6711665.7 }\n *\n * // With custom center longitude\n * const pointCentered = mercatorProjection(coord, -0.1278);\n * console.log(pointCentered.x); // Close to 0\n * ```\n *\n * @category Projections\n */\nexport function mercatorProjection(interestPoint: GeoCoord, centerLongitude: number = 0): Point{\n const r = 6371000;\n const latitude = interestPoint.latitude * Math.PI / 180;\n const longitude = interestPoint.longitude * Math.PI / 180;\n let x = r * normalizeAngleMinusPiToPi(longitude - centerLongitude * Math.PI / 180);\n let y = r * Math.log(Math.tan(Math.PI / 4 + latitude / 2));\n return {x: x, y: y};\n}\n\n/**\n * Converts a Mercator projection point back to geographic coordinates.\n *\n * @remarks\n * This is the inverse of {@link mercatorProjection}. Given a point in Mercator\n * projection space (in meters), it returns the corresponding latitude/longitude.\n *\n * @param point - The point in Mercator projection (in meters)\n * @param centerLongitude - The central meridian in degrees (must match the forward projection)\n * @returns The geographic coordinate\n *\n * @example\n * ```typescript\n * const point = { x: -14232.4, y: 6711665.7 };\n * const coord = inverseMercatorProjection(point);\n * console.log(coord); // { latitude: ~51.5, longitude: ~-0.13 }\n * ```\n *\n * @category Projections\n */\nexport function inverseMercatorProjection(point: Point, centerLongitude: number = 0): GeoCoord{\n const r = 6371000;\n const longitude = point.x / r + centerLongitude;\n const latitude = 2 * Math.atan(Math.exp(point.y / r)) - Math.PI / 2;\n return {latitude: latitude * 180 / Math.PI, longitude: longitude * 180 / Math.PI};\n}\n\n/**\n * Projects a geographic coordinate to orthographic projection.\n *\n * @remarks\n * The orthographic projection shows Earth as it would appear from space,\n * displaying one hemisphere at a time. It's useful for globe-like visualizations.\n *\n * Points on the back hemisphere (not visible from the origin viewpoint) are\n * marked as clipped.\n *\n * The projection uses Earth's mean radius of 6,371,000 meters.\n *\n * @param interestPoint - The geographic coordinate to project\n * @param origin - The center point of the hemisphere to view\n * @returns Object with clipped flag and projected coordinate\n *\n * @example\n * ```typescript\n * const origin = { latitude: 45.0, longitude: 0.0 }; // View centered on France\n * const coord = { latitude: 51.5, longitude: -0.1 }; // London\n *\n * const result = orthoProjection(coord, origin);\n * if (!result.clipped) {\n * console.log('London is visible at:', result.coord);\n * } else {\n * console.log('London is on the back of the globe');\n * }\n * ```\n *\n * @category Projections\n */\nexport function orthoProjection(interestPoint: GeoCoord, origin: GeoCoord): {clipped: boolean, coord: Point}{\n const r = 6371000;\n const latitude = interestPoint.latitude * Math.PI / 180;\n const longitude = interestPoint.longitude * Math.PI / 180;\n const x = r * Math.cos(latitude) * Math.sin(longitude - origin.longitude * Math.PI / 180);\n const y = r * (Math.cos(origin.latitude * Math.PI / 180) * Math.sin(latitude) - Math.sin(origin.latitude * Math.PI / 180) * Math.cos(latitude) * Math.cos(longitude - origin.longitude * Math.PI / 180));\n const clipped = Math.sin(origin.latitude * Math.PI / 180) * Math.sin(latitude) + Math.cos(origin.latitude * Math.PI / 180) * Math.cos(latitude) * Math.cos(longitude - origin.longitude * Math.PI / 180);\n\n return {clipped: clipped < 0, coord: {x: x, y: y}};\n}\n\n/**\n * Converts an orthographic projection point back to geographic coordinates.\n *\n * @remarks\n * This is the inverse of {@link orthoProjection}. Given a point in orthographic\n * projection space (in meters), it returns the corresponding latitude/longitude.\n *\n * @param interestPoint - The point in orthographic projection (in meters)\n * @param origin - The center point of the hemisphere (must match the forward projection)\n * @returns The geographic coordinate\n *\n * @example\n * ```typescript\n * const origin = { latitude: 45.0, longitude: 0.0 };\n * const point = { x: 100000, y: 200000 }; // Some point in projection space\n * const coord = inverseOrthoProjection(point, origin);\n * console.log(coord); // { latitude: ..., longitude: ... }\n * ```\n *\n * @category Projections\n */\nexport function inverseOrthoProjection(interestPoint: Point, origin: GeoCoord): GeoCoord {\n const r = 6371000;\n const rho = Math.sqrt(interestPoint.x * interestPoint.x + interestPoint.y * interestPoint.y);\n const c = Math.asin(rho / r);\n const latitude = Math.asin(Math.cos(c) * Math.sin(origin.latitude * Math.PI / 180) + (interestPoint.y * Math.sin(c) * Math.cos(origin.latitude * Math.PI / 180)) / rho) * 180 / Math.PI;\n const longitude = origin.longitude + Math.atan2(interestPoint.x * Math.sin(c), rho * Math.cos(c)*Math.cos(origin.latitude * Math.PI / 180) - interestPoint.y * Math.sin(c) * Math.sin(origin.latitude * Math.PI / 180)) * 180 / Math.PI;\n return {latitude: latitude, longitude: longitude};\n}\n\nfunction normalizeAngleMinusPiToPi(angle: number): number {\n // Reduce the angle\n angle = angle % (Math.PI * 2);\n\n // Force it to be in the range -π to π\n angle = (angle + 3 * Math.PI) % (2 * Math.PI) - Math.PI;\n\n return angle;\n}\n",
6
+ "import { GeoCoord } from \"./projection\";\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy representation conversion functions (c) Chris Veness 2002-2022 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong.html */\n/* www.movable-type.co.uk/scripts/js/geodesy/geodesy-library.html#dms */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Calculates an intermediate point along a great circle path.\n *\n * @remarks\n * Given two points on Earth's surface, this finds a point at a specified\n * fraction along the great circle (shortest) path between them.\n *\n * Uses the spherical interpolation formula for accurate results on a sphere.\n *\n * @param startCoord - The starting geographic coordinate\n * @param endCoord - The ending geographic coordinate\n * @param fraction - The fraction along the path (0 = start, 1 = end, 0.5 = midpoint)\n * @returns The intermediate point at the specified fraction\n *\n * @example\n * ```typescript\n * const nyc = { latitude: 40.7128, longitude: -74.0060 };\n * const london = { latitude: 51.5074, longitude: -0.1278 };\n *\n * // Find point 25% of the way from NYC to London\n * const quarter = intermediatePointOnGreatCircle(nyc, london, 0.25);\n *\n * // Find point 75% of the way\n * const threeQuarters = intermediatePointOnGreatCircle(nyc, london, 0.75);\n * ```\n *\n * @category Great Circle\n */\nexport function intermediatePointOnGreatCircle(startCoord: GeoCoord, endCoord: GeoCoord, fraction: number): GeoCoord{\n const Δφ = (endCoord.latitude - startCoord.latitude) * Math.PI / 180;\n const Δλ = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;\n const φ1 = startCoord.latitude * Math.PI / 180;\n const λ1 = startCoord.longitude * Math.PI / 180;\n const φ2 = endCoord.latitude * Math.PI / 180;\n const λ2 = endCoord.longitude * Math.PI / 180;\n const angularDistA = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +\n Math.cos(φ1) * Math.cos(φ2) *\n Math.sin(Δλ / 2) * Math.sin(Δλ / 2);\n const c = 2 * Math.atan2(Math.sqrt(angularDistA), Math.sqrt(1 - angularDistA));\n \n const a = Math.sin((1 - fraction) * c) / Math.sin(c);\n const b = Math.sin(fraction * c) / Math.sin(c);\n const x = a * Math.cos(φ1) * Math.cos(λ1) + b * Math.cos(φ2) * Math.cos(λ2);\n const y = a * Math.cos(φ1) * Math.sin(λ1) + b * Math.cos(φ2) * Math.sin(λ2);\n const z = a * Math.sin(φ1) * b * Math.sin(φ2);\n const φi = Math.atan2(z, Math.sqrt(x * x + y * y));\n const λi = Math.atan2(y, x);\n return {latitude: φi, longitude: λi};\n}\n\n/**\n * Calculates the midpoint along a great circle path.\n *\n * @remarks\n * This is a specialized, optimized version of {@link intermediatePointOnGreatCircle}\n * for finding the exact midpoint (fraction = 0.5).\n *\n * @param startCoord - The starting geographic coordinate\n * @param endCoord - The ending geographic coordinate\n * @returns The midpoint on the great circle path\n *\n * @example\n * ```typescript\n * const start = { latitude: 50.0, longitude: -5.0 };\n * const end = { latitude: 58.0, longitude: 3.0 };\n * const mid = midPointOnGreatCircle(start, end);\n * console.log('Midpoint:', mid);\n * ```\n *\n * @category Great Circle\n */\nexport function midPointOnGreatCircle(startCoord: GeoCoord, endCoord: GeoCoord): GeoCoord{\n const φ1 = startCoord.latitude * Math.PI / 180;\n const φ2 = endCoord.latitude * Math.PI / 180;\n const λ1 = startCoord.longitude * Math.PI / 180;\n const λ2 = endCoord.longitude * Math.PI / 180;\n const Bx = Math.cos(φ2) * Math.cos(λ2 - λ1);\n const By = Math.cos(φ2) * Math.sin(λ2 - λ1);\n const φ3 = Math.atan2(Math.sin(φ1) + Math.sin(φ2),\n Math.sqrt( (Math.cos(φ1) + Bx) * (Math.cos(φ1) + Bx) + By * By ) );\n const λ3 = λ1 + Math.atan2(By, Math.cos(φ1) + Bx);\n return {latitude: φ3, longitude: λ3};\n}\n\n/**\n * Calculate the initial bearing between two points on the Earth's surface. \n * (traveling along the great circle would result in different bearing from the start point to the end point)\n * \n * @param startCoord - The starting point in GeoCoord format.\n * @param endCoord - The ending point in GeoCoord format.\n * @returns The bearing in degrees.\n */\nexport function initialBearingOfGreatCircle(startCoord: GeoCoord, endCoord: GeoCoord): number{\n const φ1 = startCoord.latitude * Math.PI / 180;\n const φ2 = endCoord.latitude * Math.PI / 180;\n const λ1 = startCoord.longitude * Math.PI / 180;\n const λ2 = endCoord.longitude * Math.PI / 180;\n const y = Math.sin(λ2-λ1) * Math.cos(φ2);\n const x = Math.cos(φ1) * Math.sin(φ2) -\n Math.sin(φ1) * Math.cos(φ2)*Math.cos(λ2 - λ1);\n const θ = Math.atan2(y, x);\n const brng = (θ * 180 / Math.PI + 360) % 360; // in degrees\n return brng;\n}\n\n/**\n * Calculates the great circle distance between two points on Earth.\n *\n * @remarks\n * Uses the haversine formula to calculate the shortest distance over Earth's\n * surface between two geographic coordinates. This is the \"as-the-crow-flies\"\n * distance.\n *\n * The calculation assumes Earth's mean radius of 6,371,000 meters and treats\n * Earth as a perfect sphere.\n *\n * @param startCoord - The starting geographic coordinate\n * @param endCoord - The ending geographic coordinate\n * @returns The distance in meters\n *\n * @example\n * ```typescript\n * const nyc = { latitude: 40.7128, longitude: -74.0060 };\n * const london = { latitude: 51.5074, longitude: -0.1278 };\n *\n * const distance = greatCircleDistance(nyc, london);\n * console.log('Distance:', distance / 1000, 'km'); // ~5570 km\n * ```\n *\n * @category Great Circle\n */\nexport function greatCircleDistance(startCoord: GeoCoord, endCoord: GeoCoord): number{\n const R = 6371e3; // metres\n const φ1 = startCoord.latitude * Math.PI / 180; // φ, λ in radians\n const φ2 = endCoord.latitude * Math.PI / 180;\n const Δφ = (endCoord.latitude - startCoord.latitude) * Math.PI / 180;\n const Δλ = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;\n\n const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +\n Math.cos(φ1) * Math.cos(φ2) *\n Math.sin(Δλ / 2) * Math.sin(Δλ / 2);\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n const d = R * c; // in metres\n return d;\n} \n\n/**\n * Calculates the destination point given a start point, bearing, and distance on a great circle.\n *\n * @remarks\n * Starting from a given point and traveling along a great circle at a specific\n * initial bearing for a given distance, this calculates where you'll end up.\n *\n * Note: The bearing will change along the path (except when traveling due north/south\n * or along the equator) because great circles are not straight lines on most map projections.\n *\n * @param startCoord - The starting geographic coordinate\n * @param bearing - The initial bearing in degrees (0 = north, 90 = east, etc.)\n * @param distance - The distance to travel in meters\n * @returns The destination coordinate\n *\n * @example\n * ```typescript\n * const start = { latitude: 40.7128, longitude: -74.0060 }; // NYC\n *\n * // Travel 1000km northeast from NYC\n * const destination = destinationFromOriginOnGreatCircle(start, 45, 1000000);\n * console.log('Destination:', destination);\n * ```\n *\n * @category Great Circle\n */\nexport function destinationFromOriginOnGreatCircle(startCoord: GeoCoord, bearing: number, distance: number): GeoCoord{\n const R = 6371e3; // metres\n const φ1 = startCoord.latitude * Math.PI / 180; // φ, λ in radians\n const λ1 = startCoord.longitude * Math.PI / 180;\n const θ = bearing * Math.PI / 180;\n const d = distance / R; // angular distance in radians\n const φ2 = Math.asin(Math.sin(φ1) * Math.cos(d) +\n Math.cos(φ1) * Math.sin(d) * Math.cos(θ) );\n const λ2 = λ1 + Math.atan2(Math.sin(θ) * Math.sin(d) * Math.cos(φ1),\n Math.cos(d) - Math.sin(φ1) * Math.sin(φ2));\n return {latitude: φ2, longitude: λ2};\n}\n",
7
+ "import { GeoCoord } from \"./projection\";\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy representation conversion functions (c) Chris Veness 2002-2022 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong.html */\n/* www.movable-type.co.uk/scripts/js/geodesy/geodesy-library.html#dms */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n/**\n * Calculates the distance along a rhumb line between two points.\n *\n * @remarks\n * A rhumb line (also called loxodrome) is a path of constant bearing. Unlike great\n * circles, rhumb lines appear as straight lines on Mercator projections, making them\n * easier for navigation.\n *\n * Rhumb lines are generally slightly longer than great circle routes, except when\n * traveling due east/west along the equator or due north/south.\n *\n * Uses Earth's mean radius of 6,371,000 meters.\n *\n * @param startCoord - The starting geographic coordinate\n * @param endCoord - The ending geographic coordinate\n * @returns The distance in meters along the rhumb line\n *\n * @example\n * ```typescript\n * const start = { latitude: 50.0, longitude: -5.0 };\n * const end = { latitude: 58.0, longitude: 3.0 };\n *\n * const distance = rhumbDistance(start, end);\n * console.log('Rhumb distance:', distance / 1000, 'km');\n *\n * // Compare with great circle distance\n * const gcDistance = greatCircleDistance(start, end);\n * console.log('Great circle is', (distance - gcDistance) / 1000, 'km shorter');\n * ```\n *\n * @category Rhumb Line\n */\nexport function rhumbDistance(startCoord: GeoCoord, endCoord: GeoCoord): number{\n const R = 6371e3; // metres\n const φ1 = startCoord.latitude * Math.PI/180; // φ, λ in radians\n const φ2 = endCoord.latitude * Math.PI/180;\n const Δφ = (endCoord.latitude - startCoord.latitude) * Math.PI/180;\n let Δλ = (endCoord.longitude - startCoord.longitude) * Math.PI/180;\n const Δψ = Math.log(Math.tan(Math.PI / 4 + φ2 / 2)/Math.tan(Math.PI / 4 + φ1 / 2));\n const q = Math.abs(Δψ) > 10e-12 ? Δφ / Δψ : Math.cos(φ1); // E-W course becomes ill-conditioned with 0/0\n\n // if dLon over 180° take shorter rhumb line across the anti-meridian:\n if (Math.abs(Δλ) > Math.PI) Δλ = Δλ > 0 ? - (2 * Math.PI - Δλ) : (2 * Math.PI + Δλ);\n\n const dist = Math.sqrt(Δφ * Δφ + q * q * Δλ * Δλ) * R;\n return dist;\n}\n\n/**\n * Calculates the constant bearing along a rhumb line.\n *\n * @remarks\n * Unlike great circles where the bearing changes along the path, rhumb lines\n * maintain a constant bearing. This makes them simpler for navigation - you can\n * follow a single compass direction.\n *\n * @param startCoord - The starting geographic coordinate\n * @param endCoord - The ending geographic coordinate\n * @returns The constant bearing in degrees (0-360)\n *\n * @example\n * ```typescript\n * const start = { latitude: 50.0, longitude: -5.0 };\n * const end = { latitude: 58.0, longitude: 3.0 };\n *\n * const bearing = rhumbBearing(start, end);\n * console.log('Constant bearing:', bearing, 'degrees');\n *\n * // This bearing stays constant along the entire path\n * ```\n *\n * @category Rhumb Line\n */\nexport function rhumbBearing(startCoord: GeoCoord, endCoord: GeoCoord): number{\n const φ1 = startCoord.latitude * Math.PI / 180; // φ, λ in radians\n const φ2 = endCoord.latitude * Math.PI / 180;\n let Δλ = (endCoord.longitude - startCoord.longitude) * Math.PI / 180;\n const Δψ = Math.log(Math.tan(Math.PI / 4 + φ2 / 2) / Math.tan(Math.PI / 4 + φ1 / 2));\n\n // if dLon over 180° take shorter rhumb line across the anti-meridian:\n if (Math.abs(Δλ) > Math.PI) Δλ = Δλ > 0 ? -(2 * Math.PI-Δλ) : (2 * Math.PI + Δλ);\n\n const brng = Math.atan2(Δλ, Δψ) * 180 / Math.PI;\n return brng;\n}\n\n/**\n * Calculates the destination point given a start point, constant bearing, and distance on a rhumb line.\n *\n * @remarks\n * Starting from a given point and traveling at a constant bearing for a given\n * distance, this calculates where you'll end up. The bearing remains constant\n * throughout the journey.\n *\n * This is the rhumb line equivalent of {@link destinationFromOriginOnGreatCircle}.\n *\n * @param startCoord - The starting geographic coordinate\n * @param bearing - The constant bearing in degrees (0 = north, 90 = east, etc.)\n * @param distance - The distance to travel in meters\n * @returns The destination coordinate\n *\n * @example\n * ```typescript\n * const start = { latitude: 40.0, longitude: -74.0 };\n *\n * // Travel 500km on constant bearing of 45 degrees (northeast)\n * const destination = destinationFromOriginOnRhumbLine(start, 45, 500000);\n * console.log('Destination:', destination);\n * ```\n *\n * @category Rhumb Line\n */\nexport function destinationFromOriginOnRhumbLine(startCoord: GeoCoord, bearing: number, distance: number): GeoCoord{\n const R = 6371e3; // metres\n const φ1 = startCoord.latitude * Math.PI / 180; // φ, λ in radians\n const λ1 = startCoord.longitude * Math.PI / 180;\n const θ = bearing * Math.PI / 180;\n const d = distance;\n const δ = d / R;\n const Δφ = δ * Math.cos(θ);\n let φ2 = φ1 + Δφ;\n\n const Δψ = Math.log(Math.tan(φ2 / 2 + Math.PI / 4) / Math.tan(φ1 / 2 + Math.PI / 4));\n const q = Math.abs(Δψ) > 10e-12 ? Δφ / Δψ : Math.cos(φ1); // E-W course becomes ill-conditioned with 0/0\n\n const Δλ = δ * Math.sin(θ) / q;\n const λ2 = λ1 + Δλ;\n\n // check for some daft bugger going past the pole, normalise latitude if so\n if (Math.abs(φ2) > Math.PI / 2) φ2 = φ2 > 0 ? Math.PI - φ2 : -Math.PI - φ2;\n return {latitude: φ2, longitude: λ2};\n}\n\n/**\n * Calculates the midpoint along a rhumb line.\n *\n * @remarks\n * Finds the point exactly halfway along a rhumb line path between two points.\n *\n * @param startCoord - The starting geographic coordinate\n * @param endCoord - The ending geographic coordinate\n * @returns The midpoint on the rhumb line path\n *\n * @example\n * ```typescript\n * const start = { latitude: 50.0, longitude: -5.0 };\n * const end = { latitude: 58.0, longitude: 3.0 };\n *\n * const mid = midPointOnRhumbLine(start, end);\n * console.log('Midpoint:', mid);\n * ```\n *\n * @category Rhumb Line\n */\nexport function midPointOnRhumbLine(startCoord: GeoCoord, endCoord: GeoCoord): GeoCoord{\n let λ1 = startCoord.longitude * Math.PI / 180; \n const λ2 = endCoord.longitude * Math.PI / 180;\n const φ1 = startCoord.latitude * Math.PI / 180;\n const φ2 = endCoord.latitude * Math.PI / 180; \n if (Math.abs(λ2 - λ1) > Math.PI) λ1 += 2 * Math.PI; // crossing anti-meridian\n\n const φ3 = (φ1 + φ2) / 2;\n const f1 = Math.tan(Math.PI / 4 + φ1 / 2);\n const f2 = Math.tan(Math.PI / 4 + φ2 / 2);\n const f3 = Math.tan(Math.PI / 4 + φ3 / 2);\n let λ3 = ( (λ2 - λ1) * Math.log(f3) + λ1 * Math.log(f2) - λ2 * Math.log(f1) ) / Math.log(f2 / f1);\n\n if (!isFinite(λ3)) λ3 = (λ1 + λ2) / 2; // parallel of latitude\n return {latitude: φ3, longitude: λ3};\n}\n"
8
8
  ],
9
- "mappings": "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",
10
- "debugId": "7FC1B3305866843664756E2164756E21",
9
+ "mappings": "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",
10
+ "debugId": "ADDDACDC1E32424A64756E2164756E21",
11
11
  "names": []
12
12
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ue-too/border",
3
3
  "type": "module",
4
- "version": "0.9.4",
4
+ "version": "0.10.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/ue-too/ue-too.git"
@@ -20,7 +20,7 @@
20
20
  "./package.json": "./package.json"
21
21
  },
22
22
  "dependencies": {
23
- "@ue-too/math": "^0.9.4"
23
+ "@ue-too/math": "^0.10.0"
24
24
  },
25
25
  "main": "./index.js",
26
26
  "types": "./index.d.ts",
package/projection.d.ts CHANGED
@@ -1,12 +1,125 @@
1
1
  import { Point } from "@ue-too/math";
2
+ /**
3
+ * Geographic coordinate representing a location on Earth's surface.
4
+ *
5
+ * @remarks
6
+ * Coordinates use the WGS84 standard:
7
+ * - Latitude: -90 to 90 degrees (negative = south, positive = north)
8
+ * - Longitude: -180 to 180 degrees (negative = west, positive = east)
9
+ *
10
+ * @category Types
11
+ */
2
12
  export type GeoCoord = {
13
+ /** Longitude in degrees (-180 to 180) */
3
14
  longitude: number;
15
+ /** Latitude in degrees (-90 to 90) */
4
16
  latitude: number;
5
17
  };
18
+ /**
19
+ * Projects a geographic coordinate to Mercator projection.
20
+ *
21
+ * @remarks
22
+ * The Mercator projection is a cylindrical map projection that preserves angles
23
+ * and shapes locally. It's widely used for navigation because straight lines on
24
+ * the map represent constant bearings (rhumb lines).
25
+ *
26
+ * The projection uses Earth's mean radius of 6,371,000 meters.
27
+ *
28
+ * Note: The Mercator projection becomes increasingly distorted near the poles.
29
+ *
30
+ * @param interestPoint - The geographic coordinate to project
31
+ * @param centerLongitude - The central meridian in degrees (default: 0)
32
+ * @returns The projected point in meters from the central meridian
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const coord = { latitude: 51.5074, longitude: -0.1278 }; // London
37
+ * const point = mercatorProjection(coord);
38
+ * console.log(point); // { x: -14232.4, y: 6711665.7 }
39
+ *
40
+ * // With custom center longitude
41
+ * const pointCentered = mercatorProjection(coord, -0.1278);
42
+ * console.log(pointCentered.x); // Close to 0
43
+ * ```
44
+ *
45
+ * @category Projections
46
+ */
6
47
  export declare function mercatorProjection(interestPoint: GeoCoord, centerLongitude?: number): Point;
48
+ /**
49
+ * Converts a Mercator projection point back to geographic coordinates.
50
+ *
51
+ * @remarks
52
+ * This is the inverse of {@link mercatorProjection}. Given a point in Mercator
53
+ * projection space (in meters), it returns the corresponding latitude/longitude.
54
+ *
55
+ * @param point - The point in Mercator projection (in meters)
56
+ * @param centerLongitude - The central meridian in degrees (must match the forward projection)
57
+ * @returns The geographic coordinate
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const point = { x: -14232.4, y: 6711665.7 };
62
+ * const coord = inverseMercatorProjection(point);
63
+ * console.log(coord); // { latitude: ~51.5, longitude: ~-0.13 }
64
+ * ```
65
+ *
66
+ * @category Projections
67
+ */
7
68
  export declare function inverseMercatorProjection(point: Point, centerLongitude?: number): GeoCoord;
69
+ /**
70
+ * Projects a geographic coordinate to orthographic projection.
71
+ *
72
+ * @remarks
73
+ * The orthographic projection shows Earth as it would appear from space,
74
+ * displaying one hemisphere at a time. It's useful for globe-like visualizations.
75
+ *
76
+ * Points on the back hemisphere (not visible from the origin viewpoint) are
77
+ * marked as clipped.
78
+ *
79
+ * The projection uses Earth's mean radius of 6,371,000 meters.
80
+ *
81
+ * @param interestPoint - The geographic coordinate to project
82
+ * @param origin - The center point of the hemisphere to view
83
+ * @returns Object with clipped flag and projected coordinate
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const origin = { latitude: 45.0, longitude: 0.0 }; // View centered on France
88
+ * const coord = { latitude: 51.5, longitude: -0.1 }; // London
89
+ *
90
+ * const result = orthoProjection(coord, origin);
91
+ * if (!result.clipped) {
92
+ * console.log('London is visible at:', result.coord);
93
+ * } else {
94
+ * console.log('London is on the back of the globe');
95
+ * }
96
+ * ```
97
+ *
98
+ * @category Projections
99
+ */
8
100
  export declare function orthoProjection(interestPoint: GeoCoord, origin: GeoCoord): {
9
101
  clipped: boolean;
10
102
  coord: Point;
11
103
  };
104
+ /**
105
+ * Converts an orthographic projection point back to geographic coordinates.
106
+ *
107
+ * @remarks
108
+ * This is the inverse of {@link orthoProjection}. Given a point in orthographic
109
+ * projection space (in meters), it returns the corresponding latitude/longitude.
110
+ *
111
+ * @param interestPoint - The point in orthographic projection (in meters)
112
+ * @param origin - The center point of the hemisphere (must match the forward projection)
113
+ * @returns The geographic coordinate
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const origin = { latitude: 45.0, longitude: 0.0 };
118
+ * const point = { x: 100000, y: 200000 }; // Some point in projection space
119
+ * const coord = inverseOrthoProjection(point, origin);
120
+ * console.log(coord); // { latitude: ..., longitude: ... }
121
+ * ```
122
+ *
123
+ * @category Projections
124
+ */
12
125
  export declare function inverseOrthoProjection(interestPoint: Point, origin: GeoCoord): GeoCoord;
package/rhumbLine.d.ts CHANGED
@@ -1,5 +1,109 @@
1
1
  import { GeoCoord } from "./projection";
2
+ /**
3
+ * Calculates the distance along a rhumb line between two points.
4
+ *
5
+ * @remarks
6
+ * A rhumb line (also called loxodrome) is a path of constant bearing. Unlike great
7
+ * circles, rhumb lines appear as straight lines on Mercator projections, making them
8
+ * easier for navigation.
9
+ *
10
+ * Rhumb lines are generally slightly longer than great circle routes, except when
11
+ * traveling due east/west along the equator or due north/south.
12
+ *
13
+ * Uses Earth's mean radius of 6,371,000 meters.
14
+ *
15
+ * @param startCoord - The starting geographic coordinate
16
+ * @param endCoord - The ending geographic coordinate
17
+ * @returns The distance in meters along the rhumb line
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const start = { latitude: 50.0, longitude: -5.0 };
22
+ * const end = { latitude: 58.0, longitude: 3.0 };
23
+ *
24
+ * const distance = rhumbDistance(start, end);
25
+ * console.log('Rhumb distance:', distance / 1000, 'km');
26
+ *
27
+ * // Compare with great circle distance
28
+ * const gcDistance = greatCircleDistance(start, end);
29
+ * console.log('Great circle is', (distance - gcDistance) / 1000, 'km shorter');
30
+ * ```
31
+ *
32
+ * @category Rhumb Line
33
+ */
2
34
  export declare function rhumbDistance(startCoord: GeoCoord, endCoord: GeoCoord): number;
35
+ /**
36
+ * Calculates the constant bearing along a rhumb line.
37
+ *
38
+ * @remarks
39
+ * Unlike great circles where the bearing changes along the path, rhumb lines
40
+ * maintain a constant bearing. This makes them simpler for navigation - you can
41
+ * follow a single compass direction.
42
+ *
43
+ * @param startCoord - The starting geographic coordinate
44
+ * @param endCoord - The ending geographic coordinate
45
+ * @returns The constant bearing in degrees (0-360)
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const start = { latitude: 50.0, longitude: -5.0 };
50
+ * const end = { latitude: 58.0, longitude: 3.0 };
51
+ *
52
+ * const bearing = rhumbBearing(start, end);
53
+ * console.log('Constant bearing:', bearing, 'degrees');
54
+ *
55
+ * // This bearing stays constant along the entire path
56
+ * ```
57
+ *
58
+ * @category Rhumb Line
59
+ */
3
60
  export declare function rhumbBearing(startCoord: GeoCoord, endCoord: GeoCoord): number;
61
+ /**
62
+ * Calculates the destination point given a start point, constant bearing, and distance on a rhumb line.
63
+ *
64
+ * @remarks
65
+ * Starting from a given point and traveling at a constant bearing for a given
66
+ * distance, this calculates where you'll end up. The bearing remains constant
67
+ * throughout the journey.
68
+ *
69
+ * This is the rhumb line equivalent of {@link destinationFromOriginOnGreatCircle}.
70
+ *
71
+ * @param startCoord - The starting geographic coordinate
72
+ * @param bearing - The constant bearing in degrees (0 = north, 90 = east, etc.)
73
+ * @param distance - The distance to travel in meters
74
+ * @returns The destination coordinate
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const start = { latitude: 40.0, longitude: -74.0 };
79
+ *
80
+ * // Travel 500km on constant bearing of 45 degrees (northeast)
81
+ * const destination = destinationFromOriginOnRhumbLine(start, 45, 500000);
82
+ * console.log('Destination:', destination);
83
+ * ```
84
+ *
85
+ * @category Rhumb Line
86
+ */
4
87
  export declare function destinationFromOriginOnRhumbLine(startCoord: GeoCoord, bearing: number, distance: number): GeoCoord;
88
+ /**
89
+ * Calculates the midpoint along a rhumb line.
90
+ *
91
+ * @remarks
92
+ * Finds the point exactly halfway along a rhumb line path between two points.
93
+ *
94
+ * @param startCoord - The starting geographic coordinate
95
+ * @param endCoord - The ending geographic coordinate
96
+ * @returns The midpoint on the rhumb line path
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * const start = { latitude: 50.0, longitude: -5.0 };
101
+ * const end = { latitude: 58.0, longitude: 3.0 };
102
+ *
103
+ * const mid = midPointOnRhumbLine(start, end);
104
+ * console.log('Midpoint:', mid);
105
+ * ```
106
+ *
107
+ * @category Rhumb Line
108
+ */
5
109
  export declare function midPointOnRhumbLine(startCoord: GeoCoord, endCoord: GeoCoord): GeoCoord;