gipy : compute distances

pull/2234/head
frederic wagner 2022-07-13 11:53:53 +02:00
parent a1705964c2
commit 81226a34d6
1 changed files with 93 additions and 76 deletions

View File

@ -10,36 +10,48 @@
var lat = null;
var lon = null;
var refresh_needed = false;
class Path {
constructor(filename) {
let buffer = require("Storage").readArrayBuffer(filename);
this.points = Float64Array(buffer);
let total_distance = 0.0;
this.on_segments(function (p1, p2, i) {
total_distance += p1.distance(p2);
}, 0, this.len-1);
this.total_distance = total_distance;
}
// start is index of first wanted segment
// end is 1 after index of last wanted segment
on_segments(op, start, end) {
let previous_point = null;
for(let i = start ; i < end + 1 ; i++) {
let point = new Point(this.points[2*i], this.points[2*i+1]);
if (previous_point !== null) {
op(previous_point, point, i);
}
previous_point = point;
}
}
point(index) {
let lon = this.points[2*index];
let lat = this.points[2*index+1];
return new Point(lon, lat);
}
// return index of segment which is nearest from point
nearest_segment(point, start, end) {
let previous_point = null;
let min_index = 0;
let min_distance = Number.MAX_VALUE;
for(let i = Math.max(0, start) ; i < Math.min(this.len, end) ; i++) {
let current_point = this.point(i);
if (previous_point !== null) {
let distance = point.distance_to_segment(previous_point, current_point);
this.on_segments(function (p1, p2, i) {
let distance = point.fake_distance_to_segment(p1, p2);
if (distance <= min_distance) {
min_distance = distance;
min_index = i-1;
}
}
previous_point = current_point;
}
}, start, end);
return min_index;
}
get len() {
@ -77,9 +89,24 @@ class Point {
return this.lon * other_point.lon + this.lat * other_point.lat;
}
distance(other_point) {
//see https://www.movable-type.co.uk/scripts/latlong.html
const R = 6371e3; // metres
const phi1 = this.lat * Math.PI/180;
const phi2 = other_point.lat * Math.PI/180;
const deltaphi = (other_point.lat-this.lat) * Math.PI/180;
const deltalambda = (other_point.lon-this.lon) * Math.PI/180;
const a = Math.sin(deltaphi/2) * Math.sin(deltaphi/2) +
Math.cos(phi1) * Math.cos(phi2) *
Math.sin(deltalambda/2) * Math.sin(deltalambda/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return R * c; // in metres
}
fake_distance(other_point) {
return Math.sqrt(this.length_squared(other_point));
}
distance_to_segment(v, w) {
fake_distance_to_segment(v, w) {
// from : https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
// Return minimum distance between line segment vw and point p
let l2 = v.length_squared(w); // i.e. |w-v|^2 - avoid a sqrt
@ -87,96 +114,86 @@ class Point {
return this.distance(v); // v == w case
}
// Consider the line extending the segment, parameterized as v + t (w - v).
// We find projection of point p onto the line.
// We find projection of point p onto the line.
// It falls where t = [(p-v) . (w-v)] / |w-v|^2
// We clamp t from [0,1] to handle points outside the segment vw.
let t = Math.max(0, Math.min(1, (this.minus(v)).dot(w.minus(v)) / l2));
let projection = v.plus((w.minus(v)).times(t)); // Projection falls on the segment
return this.distance(projection);
return this.fake_distance(projection);
}
}
function display(path) {
if (!refresh_needed) {
return;
}
refresh_needed = false;
g.clear();
g.setColor(g.theme.fg);
// let next_segment = path.nearest_segment(new Point(lon, lat), current_segment-2, current_segment+3);
current_segment = path.nearest_segment(new Point(lon, lat), 0, path.len);
let previous_point = null;
current_segment = path.nearest_segment(new Point(lon, lat), 0, path.len-1);
let start = Math.max(current_segment - 5, 0);
let end = Math.min(current_segment + 7, path.len);
for (let i=start ; i < end ; i++) {
let point = path.point(i);
let px = point.screen_x();
let py = point.screen_y();
if (previous_point !== null) {
if (i == current_segment + 1) {
g.setColor(0.0, 1.0, 0.0);
} else {
g.setColor(1.0, 0.0, 0.0);
}
g.drawLine(
previous_point.screen_x(),
previous_point.screen_y(),
px,
py
);
let end = Math.min(current_segment + 7, path.len-1);
path.on_segments(function(p1, p2, i) {
let px = p2.screen_x();
let py = p2.screen_y();
if (i == current_segment + 1) {
g.setColor(0.0, 1.0, 0.0);
} else {
g.setColor(1.0, 0.0, 0.0);
}
g.setColor(g.theme.fg2);
g.fillCircle(px, py, 4);
g.drawLine(
p1.screen_x(),
p1.screen_y(),
px,
py
);
g.setColor(g.theme.fg);
g.fillCircle(px, py, 4);
g.setColor(g.theme.bg);
g.fillCircle(px, py, 3);
previous_point = point;
}
}, 0, path.len-1);
g.setColor(g.theme.fgH);
g.fillCircle(172/2, 172/2, 5);
g.setFont("6x8:2").drawString(("distance "+(Math.round(path.total_distance/100)/10))+" km",0,30);
Bangle.drawWidgets();
}
Bangle.loadWidgets();
let path = new Path("test.gpc");
var current_segment = path.nearest_segment(new Point(lon, lat), 0, Number.MAX_VALUE);
var current_segment = path.nearest_segment(new Point(lon, lat), 0, path.len-1);
// let fake_gps_point = 0.0;
// function simulate_gps(path) {
// let point_index = Math.floor(fake_gps_point);
// if (point_index >= path.len) {
// return;
// }
// let p1 = path.point(point_index);
// let p2 = path.point(point_index+1);
// let alpha = fake_gps_point - point_index;
// lon = (1-alpha)*p1.lon + alpha*p2.lon;
// lat = (1-alpha)*p1.lat + alpha*p2.lat;
// fake_gps_point += 0.2;
// display(path);
// }
// setInterval(simulate_gps, 500, path);
function set_coordinates(data) {
let old_lat = lat;
if (!isNaN(data.lat)) {
lat = data.lat;
}
let old_lon = lon;
if (!isNaN(data.lon)) {
lon = data.lon;
}
if ((old_lat != lat)||(old_lon != lon)) {
refresh_needed = true;
let fake_gps_point = 0.0;
function simulate_gps(path) {
let point_index = Math.floor(fake_gps_point);
if (point_index >= path.len) {
return;
}
let p1 = path.point(point_index);
let p2 = path.point(point_index+1);
let alpha = fake_gps_point - point_index;
lon = (1-alpha)*p1.lon + alpha*p2.lon;
lat = (1-alpha)*p1.lat + alpha*p2.lat;
fake_gps_point += 0.2;
display(path);
}
Bangle.setGPSPower(true, "gipy");
Bangle.on('GPS', set_coordinates);
setInterval(simulate_gps, 500, path);
setInterval(display, 1000, path);
// function set_coordinates(data) {
// let old_lat = lat;
// if (!isNaN(data.lat)) {
// lat = data.lat;
// }
// let old_lon = lon;
// if (!isNaN(data.lon)) {
// lon = data.lon;
// }
// if ((old_lat != lat)||(old_lon != lon)) {
// display(path);
// }
// }
// Bangle.setGPSPower(true, "gipy");
// Bangle.on('GPS', set_coordinates);