igc-xc-score
Version:
igc-xc-score is a paragliding and hang-gliding XC scoring program in vanilla JS
217 lines (190 loc) • 5.82 kB
JavaScript
'use strict';
import * as util from './util.js';
import * as vincentys from './vincentys.js';
export class Point {
constructor(x, y) {
if (Array.isArray(x)) {
this.x = +x[y].longitude;
this.y = +x[y].latitude;
this.r = +y;
} else {
this.x = x;
this.y = y;
}
}
geojson(id, properties) {
const feature = {
type: 'Feature',
id,
properties: properties || {},
geometry: {
type: 'Point',
coordinates: [this.x, this.y]
}
};
return feature;
}
/* c8 ignore next 3 */
toString() {
return JSON.stringify(this.geojson());
}
intersects(other) {
if (other instanceof Point)
return (this.x == other.x && this.y == other.y);
if (other instanceof Box)
return other.intersects(this);
throw new TypeError('other must be either Point or Box');
}
distanceEarth(p) {
return this.distanceEarthFCC(p);
}
distanceEarthFCC(p) {
const df = (p.y - this.y);
const dg = (p.x - this.x);
const fm = util.radians((this.y + p.y) / 2);
// Speed up cos computation using:
// - cos(2x) = 2 * cos(x)^2 - 1
// - cos(a+b) = 2 * cos(a)cos(b) - cos(a-b)
const cosfm = Math.cos(fm);
const cos2fm = 2 * cosfm * cosfm - 1;
const cos3fm = cosfm * ( 2 * cos2fm - 1);
const cos4fm = 2 * cos2fm * cos2fm - 1;
const cos5fm = 2 * cos2fm * cos3fm - cosfm;
const k1 = 111.13209 - 0.566605 * cos2fm + 0.00120 * cos4fm;
const k2 = 111.41513 * cosfm - 0.09455 * cos3fm + 0.00012 * cos5fm;
const d = Math.sqrt((k1 * df) * (k1 * df) + (k2 * dg) * (k2 * dg));
return d;
}
/* c8 ignore next 5 */
distanceEarthRev(dx, dy) {
const lon = this.x + util.degrees((dx / util.REarth) / Math.cos(util.radians(this.y)));
const lat = this.y + util.degrees(dy / util.REarth);
return new Point(lon, lat);
}
distanceEarthVincentys(p) {
return vincentys.inverse(this, p).distance;
}
}
export class Range {
constructor(start, end) {
this.start = start;
this.end = end;
if (end < start)
throw new Error('start should be before end');
}
count() {
return this.end - this.start + 1;
}
center() {
return this.start + Math.floor((this.end - this.start) / 2);
}
left() {
return new Range(this.start, this.center());
}
right() {
return new Range(this.start + Math.ceil((this.end - this.start) / 2), this.end);
}
contains(p) {
return this.start <= p && p <= this.end;
}
/* c8 ignore next 3 */
toString() {
return `${this.start}:${this.end}`;
}
}
export class Box {
constructor(a, b, c, d) {
if (a instanceof Range) {
this.x1 = Infinity;
this.y1 = Infinity;
this.x2 = -Infinity;
this.y2 = -Infinity;
for (let i = a.start; i <= a.end; i++) {
this.x1 = Math.min(b.flightPoints[i].x, this.x1);
this.y1 = Math.min(b.flightPoints[i].y, this.y1);
this.x2 = Math.max(b.flightPoints[i].x, this.x2);
this.y2 = Math.max(b.flightPoints[i].y, this.y2);
}
} else {
this.x1 = a;
this.y1 = b;
this.x2 = c;
this.y2 = d;
}
}
vertices() {
return [
new Point(this.x1, this.y1),
new Point(this.x2, this.y1),
new Point(this.x2, this.y2),
new Point(this.x1, this.y2)
];
}
intersects(other) {
if (other instanceof Point)
return (this.x1 <= other.x && this.y1 <= other.y && this.x2 >= other.x && this.y2 >= other.y);
if (this.x1 > other.x2 || this.x2 < other.x1 || this.y1 > other.y2 || this.y2 < other.y1)
return false;
return true;
}
area() {
return Math.abs((this.x2 - this.x1) * (this.y2 - this.y1));
}
/* c8 ignore next 25 */
distance(other) {
if (this.intersects(other))
return 0;
let x1 = this.x1;
let x2 = this.x1;
let y1 = this.y1;
let y2 = this.y1;
if (this.x1 > other.x2) {
x2 = other.x2;
} else if (this.x2 < other.x1) {
x1 = this.x2;
x2 = other.x1;
}
if (this.y1 < other.y2) {
y2 = other.y2;
} else if (this.y2 > other.y1) {
y1 = this.y2;
y2 = other.y1;
}
return new Point(x1, y1).distanceEarth(new Point(x2, y2));
}
/* These are debugging aids */
/* c8 ignore start */
geojson(id, properties) {
const feature = {
type: 'Feature',
id,
properties: properties || {},
geometry: {
type: 'Polygon',
coordinates: [[
[this.x1, this.y1],
[this.x1, this.y2],
[this.x2, this.y2],
[this.x2, this.y1],
[this.x1, this.y1],
]]
}
};
return feature;
}
geojson_collection(boxes) {
let features = [];
for (let i = 0; i < boxes.length; i++) {
features.push(boxes[i].geojson(i, { id: i }));
}
let collection = {
type: 'FeatureCollection',
features
};
return collection;
}
toString() {
return JSON.stringify(this.geojson());
}
/* c8 ignore stop */
}