forked from FOSS/BangleApps
commit
19f0160f55
|
@ -3,4 +3,5 @@ apps/banglerun/rollup.config.js
|
|||
apps/schoolCalendar/fullcalendar/main.js
|
||||
apps/authentiwatch/qr_packed.js
|
||||
apps/qrcode/qr-scanner.umd.min.js
|
||||
apps/gipy/pkg/gpconv.js
|
||||
*.test.js
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
0.01: Initial code
|
||||
|
||||
0.05:
|
||||
* We now buzz before reaching a waypoint.
|
||||
* Display is only updated when not locked.
|
||||
* We detect leaving path and finding path again.
|
||||
* We display remaining distance to next point.
|
||||
|
||||
0.06:
|
||||
* Special display for points with steep turns.
|
||||
* Buzz on points with steep turns and unlock.
|
||||
* Losing gps is now displayed.
|
||||
|
||||
0.07:
|
||||
* We now use orientation to detect current segment
|
||||
when segments overlap going in both directions.
|
||||
* File format is now versioned.
|
||||
|
||||
0.08:
|
||||
* Don't use gps course anymore but figure it from previous positions.
|
||||
* Bugfix: path colors are back.
|
||||
* Always buzz when reaching waypoint even if unlocked.
|
||||
|
||||
0.09:
|
||||
* We now display interest points.
|
||||
* Menu to choose which file to load.
|
||||
|
||||
0.10:
|
||||
* Display performances enhancement.
|
||||
* Waypoints information is embedded in file and extracted from comments on
|
||||
points.
|
||||
* Bugfix in map display (last segment was missing + wrong colors).
|
||||
* Waypoint detections using OSM + sharp angles
|
||||
* New algorith for direction detection
|
||||
|
||||
0.11:
|
||||
* Better fonts (more free space, still readable).
|
||||
* Display direction to nearest point when lost.
|
||||
* Display average speed.
|
||||
* Turn off gps when locked and between points
|
||||
|
||||
0.12:
|
||||
* Bugfix in speed computation.
|
||||
* Bugfix in current segment detection.
|
||||
* Bugfix : lost direction.
|
||||
* Larger fonts.
|
||||
* Detecting next point correctly when going back.
|
||||
|
||||
0.13:
|
||||
* Bugfix in lost direction.
|
||||
* Buzzing 100m ahead instead of 50m.
|
||||
* Detect sharp turns.
|
||||
* Display instant speed.
|
||||
* New instant speed algorithm.
|
||||
* Bugfix for remaining distance when going back.
|
||||
|
||||
0.14:
|
||||
* Detect starting distance to compute a good average speed.
|
||||
* Settings
|
||||
* Account for breaks in average speed.
|
||||
|
||||
0.15:
|
||||
* Record traveled distance to get a good average speed.
|
||||
* Breaks (low speed) will not count in average speed.
|
||||
* Bugfix in average speed.
|
|
@ -0,0 +1,109 @@
|
|||
# Gipy
|
||||
|
||||
Gipy allows you to follow gpx traces on your watch.
|
||||
|
||||

|
||||
|
||||
|
||||
It is for now meant for bicycling and not hiking
|
||||
(it uses your movement to figure out your orientation
|
||||
and walking is too slow).
|
||||
|
||||
It is untested on Banglejs1. If you can try it, you would be welcome.
|
||||
|
||||
This software is not perfect but surprisingly useful.
|
||||
|
||||
## Features
|
||||
|
||||
It provides the following features :
|
||||
|
||||
- display the path with current position from gps
|
||||
- detects and buzzes if you leave the path
|
||||
- buzzes before sharp turns
|
||||
- buzzes before nodes with comments
|
||||
(for example when you need to turn in https://mapstogpx.com/)
|
||||
- display instant / average speed
|
||||
- display distance to next node
|
||||
- display additional data from openstreetmap :
|
||||
- water points
|
||||
- toilets
|
||||
- artwork
|
||||
- bakeries
|
||||
|
||||
optionally it can also:
|
||||
|
||||
- try to turn off gps between crossroads to save battery
|
||||
|
||||
## Usage
|
||||
|
||||
### Preparing the file
|
||||
|
||||
You first need to have a trace file in *gpx* format.
|
||||
Usually I download from [komoot](https://www.komoot.com/) or I export
|
||||
from google maps using [mapstogpx](https://mapstogpx.com/).
|
||||
|
||||
Note that *mapstogpx* has a super nice feature in its advanced settings.
|
||||
You can turn on 'next turn info' and be warned by the watch when you need to turn.
|
||||
|
||||
Once you have your gpx file you need to convert it to *gpc* which is my custom file format.
|
||||
They are smaller than gpx and reduce the number of computations left to be done on the watch.
|
||||
|
||||
Just click the disk icon and select your gpx file.
|
||||
This will request additional information from openstreetmap.
|
||||
Your path will be displayed in svg.
|
||||
|
||||
### Starting Gipy
|
||||
|
||||
Once you start gipy you will have a menu for selecting your trace (if more than one).
|
||||
Choose the one you want and here you go :
|
||||
|
||||

|
||||
|
||||
On your screen you can see :
|
||||
|
||||
- yourself (the big black dot)
|
||||
- the path (the top of the screen is in front of you)
|
||||
- if needed a projection of yourself on the path (small black dot)
|
||||
- extremities of segments as white dots
|
||||
- turning points as doubled white dots
|
||||
- some text on the left (from top to bottom) :
|
||||
* current time
|
||||
* left distance till end of current segment
|
||||
* distance from start of path / path length
|
||||
* average speed / instant speed
|
||||
- interest points from openstreetmap as color dots :
|
||||
* red : bakery
|
||||
* deep blue : water point
|
||||
* cyan : toilets (often doubles as water point)
|
||||
* green : artwork
|
||||
- a *turn* indicator on the top right when you reach a turning point
|
||||
- a *gps* indicator (blinking) on the top right if you lose gps signal
|
||||
- a *lost* indicator on the top right if you stray too far away from path
|
||||
- a black segment extending from you when you are lost, indicating the rough direction of where to go
|
||||
|
||||
### Settings
|
||||
|
||||
Few settings for now (feel free to suggest me more) :
|
||||
|
||||
- keep gps alive : if turned off, will try to save battery by turning the gps off on long segments
|
||||
- max speed : used to compute how long to turn the gps off
|
||||
|
||||
### Caveats
|
||||
|
||||
It is good to use but you should know :
|
||||
|
||||
- the gps might take a long time to start initially (see the assisted gps update app).
|
||||
- gps signal is noisy : there is therefore a small delay for instant speed. sometimes you may jump somewhere else.
|
||||
- your gpx trace has been decimated and approximated : the **REAL PATH** might be **A FEW METERS AWAY**
|
||||
- sometimes the watch will tell you that you are lost but you are in fact on the path.
|
||||
- battery saving by turning off gps is not very well tested (disabled by default).
|
||||
- buzzing does not always work: when there is a high load on the watch, the buzzes might just never happen :-(.
|
||||
- buzzes are not strong enough to be always easily noticed.
|
||||
- be careful when **GOING DOWNHILL AT VERY HIGH SPEED**. I already missed a few turning points and by the time I realized it,
|
||||
I had to go back uphill by quite a distance.
|
||||
|
||||
## Creator
|
||||
|
||||
Feel free to give me feedback : is it useful for you ? what other features would you like ?
|
||||
|
||||
frederic.wagner@imag.fr
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
* bugs
|
||||
|
||||
- when exactly on turn, distance to next point is still often 50m
|
||||
-----> it does not buzz very often on turns
|
||||
|
||||
- when going backwards we have a tendencing to get a wrong current_segment
|
||||
|
||||
* additional features
|
||||
|
||||
- config screen
|
||||
- are we on foot (and should use compass)
|
||||
|
||||
- we need to buzz 200m before sharp turns (or even better, 30seconds)
|
||||
(and look at more than next point)
|
||||
|
||||
- display distance to next water/toilet ?
|
||||
- dynamic map rescale
|
||||
- display scale (100m)
|
||||
|
||||
- compress path ?
|
||||
|
||||
* misc
|
||||
|
||||
- code is becoming messy
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkBiIA/AE8VqoAGCy1RiN3CyYuBi93uIXJIBV3AAIuMBY4XjQ5YXPRAIAEOwIABPBC4LF54wGF6IwFC5jWGIwxIJC4xJFgDuJJAxJFC6TEIJBzEHGCIYPGA5JQC44YPGBBJKY4gwRfQL4DGCL4GGCAXPGAxGBAAJIMGAwWCGCoWGC55HHJB5HIC8pGDSChfXC5AWIL5ynOC45GJC4h3IIyYwCFxwADgB1SC44uSC4guSAH4Ab"))
|
|
@ -0,0 +1,766 @@
|
|||
let simulated = false;
|
||||
let file_version = 3;
|
||||
let code_key = 47490;
|
||||
|
||||
var settings = Object.assign(
|
||||
{
|
||||
keep_gps_alive: true,
|
||||
max_speed: 35,
|
||||
},
|
||||
require("Storage").readJSON("gipy.json", true) || {}
|
||||
);
|
||||
|
||||
let interests_colors = [
|
||||
0xf800, // Bakery, red
|
||||
0x001f, // DrinkingWater, blue
|
||||
0x07ff, // Toilets, cyan
|
||||
0x07e0, // Artwork, green
|
||||
];
|
||||
|
||||
function binary_search(array, x) {
|
||||
let start = 0,
|
||||
end = array.length - 1;
|
||||
|
||||
while (start <= end) {
|
||||
let mid = Math.floor((start + end) / 2);
|
||||
if (array[mid] < x) start = mid + 1;
|
||||
else end = mid - 1;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
class Status {
|
||||
constructor(path) {
|
||||
this.path = path;
|
||||
this.on_path = false; // are we on the path or lost ?
|
||||
this.position = null; // where we are
|
||||
this.adjusted_cos_direction = null; // cos of where we look at
|
||||
this.adjusted_sin_direction = null; // sin of where we look at
|
||||
this.current_segment = null; // which segment is closest
|
||||
this.reaching = null; // which waypoint are we reaching ?
|
||||
this.distance_to_next_point = null; // how far are we from next point ?
|
||||
this.paused_time = 0.0; // how long did we stop (stops don't count in avg speed)
|
||||
this.paused_since = getTime();
|
||||
|
||||
let r = [0];
|
||||
// let's do a reversed prefix computations on all distances:
|
||||
// loop on all segments in reversed order
|
||||
let previous_point = null;
|
||||
for (let i = this.path.len - 1; i >= 0; i--) {
|
||||
let point = this.path.point(i);
|
||||
if (previous_point !== null) {
|
||||
r.unshift(r[0] + point.distance(previous_point));
|
||||
}
|
||||
previous_point = point;
|
||||
}
|
||||
this.remaining_distances = r; // how much distance remains at start of each segment
|
||||
this.starting_time = this.paused_since; // time we start
|
||||
this.advanced_distance = 0.0;
|
||||
this.gps_coordinates_counter = 0; // how many coordinates did we receive
|
||||
this.old_points = [];
|
||||
this.old_times = [];
|
||||
}
|
||||
new_position_reached(position) {
|
||||
// we try to figure out direction by looking at previous points
|
||||
// instead of the gps course which is not very nice.
|
||||
this.gps_coordinates_counter += 1;
|
||||
let now = getTime();
|
||||
this.old_points.push(position);
|
||||
this.old_times.push(now);
|
||||
|
||||
if (this.old_points.length == 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let last_point = this.old_points[this.old_points.length - 1];
|
||||
let oldest_point = this.old_points[0];
|
||||
|
||||
// every 7 points we count the distance
|
||||
if (this.gps_coordinates_counter % 7 == 0) {
|
||||
let distance = last_point.distance(oldest_point);
|
||||
if (distance < 150.0) {
|
||||
// to avoid gps glitches
|
||||
this.advanced_distance += distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.old_points.length == 8) {
|
||||
let p1 = this.old_points[0]
|
||||
.plus(this.old_points[1])
|
||||
.plus(this.old_points[2])
|
||||
.plus(this.old_points[3])
|
||||
.times(1 / 4);
|
||||
let p2 = this.old_points[4]
|
||||
.plus(this.old_points[5])
|
||||
.plus(this.old_points[6])
|
||||
.plus(this.old_points[7])
|
||||
.times(1 / 4);
|
||||
let t1 = (this.old_times[1] + this.old_times[2]) / 2;
|
||||
let t2 = (this.old_times[5] + this.old_times[6]) / 2;
|
||||
this.instant_speed = p1.distance(p2) / (t2 - t1);
|
||||
this.old_points.shift();
|
||||
this.old_times.shift();
|
||||
} else {
|
||||
this.instant_speed =
|
||||
oldest_point.distance(last_point) / (now - this.old_times[0]);
|
||||
|
||||
// update paused time if we are too slow
|
||||
if (this.instant_speed < 2) {
|
||||
if (this.paused_since === null) {
|
||||
this.paused_since = now;
|
||||
}
|
||||
} else {
|
||||
if (this.paused_since !== null) {
|
||||
this.paused_time += now - this.paused_since;
|
||||
this.paused_since = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// let's just take angle of segment between newest point and a point a bit before
|
||||
let previous_index = this.old_points.length - 3;
|
||||
if (previous_index < 0) {
|
||||
previous_index = 0;
|
||||
}
|
||||
let diff = position.minus(this.old_points[previous_index]);
|
||||
let angle = Math.atan2(diff.lat, diff.lon);
|
||||
return angle;
|
||||
}
|
||||
update_position(new_position, maybe_direction) {
|
||||
let direction = this.new_position_reached(new_position);
|
||||
if (direction === null) {
|
||||
if (maybe_direction === null) {
|
||||
return;
|
||||
} else {
|
||||
direction = maybe_direction;
|
||||
}
|
||||
}
|
||||
|
||||
this.adjusted_cos_direction = Math.cos(-direction - Math.PI / 2.0);
|
||||
this.adjusted_sin_direction = Math.sin(-direction - Math.PI / 2.0);
|
||||
cos_direction = Math.cos(direction);
|
||||
sin_direction = Math.sin(direction);
|
||||
this.position = new_position;
|
||||
|
||||
// detect segment we are on now
|
||||
let res = this.path.nearest_segment(
|
||||
this.position,
|
||||
Math.max(0, this.current_segment - 1),
|
||||
Math.min(this.current_segment + 2, this.path.len - 1),
|
||||
cos_direction,
|
||||
sin_direction
|
||||
);
|
||||
let orientation = res[0];
|
||||
let next_segment = res[1];
|
||||
|
||||
if (this.is_lost(next_segment)) {
|
||||
// it did not work, try anywhere
|
||||
res = this.path.nearest_segment(
|
||||
this.position,
|
||||
0,
|
||||
this.path.len - 1,
|
||||
cos_direction,
|
||||
sin_direction
|
||||
);
|
||||
orientation = res[0];
|
||||
next_segment = res[1];
|
||||
}
|
||||
// now check if we strayed away from path or back to it
|
||||
let lost = this.is_lost(next_segment);
|
||||
if (this.on_path == lost) {
|
||||
// if status changes
|
||||
if (lost) {
|
||||
Bangle.buzz(); // we lost path
|
||||
setTimeout(() => Bangle.buzz(), 500);
|
||||
setTimeout(() => Bangle.buzz(), 1000);
|
||||
setTimeout(() => Bangle.buzz(), 1500);
|
||||
}
|
||||
this.on_path = !lost;
|
||||
}
|
||||
|
||||
this.current_segment = next_segment;
|
||||
|
||||
// check if we are nearing the next point on our path and alert the user
|
||||
let next_point = this.current_segment + (1 - orientation);
|
||||
this.distance_to_next_point = Math.ceil(
|
||||
this.position.distance(this.path.point(next_point))
|
||||
);
|
||||
|
||||
// disable gps when far from next point and locked
|
||||
if (Bangle.isLocked() && !settings.keep_gps_alive) {
|
||||
let time_to_next_point =
|
||||
(this.distance_to_next_point * 3.6) / settings.max_speed;
|
||||
if (time_to_next_point > 60) {
|
||||
Bangle.setGPSPower(false, "gipy");
|
||||
setTimeout(function () {
|
||||
Bangle.setGPSPower(true, "gipy");
|
||||
}, time_to_next_point);
|
||||
}
|
||||
}
|
||||
if (this.reaching != next_point && this.distance_to_next_point <= 100) {
|
||||
this.reaching = next_point;
|
||||
let reaching_waypoint = this.path.is_waypoint(next_point);
|
||||
if (reaching_waypoint) {
|
||||
Bangle.buzz();
|
||||
setTimeout(() => Bangle.buzz(), 500);
|
||||
setTimeout(() => Bangle.buzz(), 1000);
|
||||
setTimeout(() => Bangle.buzz(), 1500);
|
||||
if (Bangle.isLocked()) {
|
||||
Bangle.setLocked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// re-display
|
||||
this.display(orientation);
|
||||
}
|
||||
remaining_distance(orientation) {
|
||||
let remaining_in_correct_orientation =
|
||||
this.remaining_distances[this.current_segment + 1] +
|
||||
this.position.distance(this.path.point(this.current_segment + 1));
|
||||
|
||||
if (orientation == 0) {
|
||||
return remaining_in_correct_orientation;
|
||||
} else {
|
||||
return this.remaining_distances[0] - remaining_in_correct_orientation;
|
||||
}
|
||||
}
|
||||
is_lost(segment) {
|
||||
let distance_to_nearest = this.position.distance_to_segment(
|
||||
this.path.point(segment),
|
||||
this.path.point(segment + 1)
|
||||
);
|
||||
return distance_to_nearest > 50;
|
||||
}
|
||||
display(orientation) {
|
||||
g.clear();
|
||||
this.display_map();
|
||||
|
||||
this.display_interest_points();
|
||||
this.display_stats(orientation);
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
display_interest_points() {
|
||||
// this is the algorithm in case we have a lot of interest points
|
||||
// let's draw all points for 5 segments centered on current one
|
||||
let starting_group = Math.floor(Math.max(this.current_segment - 2, 0) / 3);
|
||||
let ending_group = Math.floor(
|
||||
Math.min(this.current_segment + 2, this.path.len - 2) / 3
|
||||
);
|
||||
let starting_bucket = binary_search(
|
||||
this.path.interests_starts,
|
||||
starting_group
|
||||
);
|
||||
let ending_bucket = binary_search(
|
||||
this.path.interests_starts,
|
||||
ending_group + 0.5
|
||||
);
|
||||
// we have 5 points per bucket
|
||||
let end_index = Math.min(
|
||||
this.path.interests_types.length - 1,
|
||||
ending_bucket * 5
|
||||
);
|
||||
for (let i = starting_bucket * 5; i <= end_index; i++) {
|
||||
let index = this.path.interests_on_path[i];
|
||||
let interest_point = this.path.interest_point(index);
|
||||
let color = this.path.interest_color(i);
|
||||
let c = interest_point.coordinates(
|
||||
this.position,
|
||||
this.adjusted_cos_direction,
|
||||
this.adjusted_sin_direction
|
||||
);
|
||||
g.setColor(color).fillCircle(c[0], c[1], 5);
|
||||
}
|
||||
}
|
||||
display_stats(orientation) {
|
||||
let remaining_distance = this.remaining_distance(orientation);
|
||||
let rounded_distance = Math.round(remaining_distance / 100) / 10;
|
||||
let total = Math.round(this.remaining_distances[0] / 100) / 10;
|
||||
let now = new Date();
|
||||
let minutes = now.getMinutes().toString();
|
||||
if (minutes.length < 2) {
|
||||
minutes = "0" + minutes;
|
||||
}
|
||||
let hours = now.getHours().toString();
|
||||
g.setFont("6x8:2")
|
||||
.setFontAlign(-1, -1, 0)
|
||||
.setColor(g.theme.fg)
|
||||
.drawString(hours + ":" + minutes, 0, 30);
|
||||
|
||||
g.setFont("6x8:2").drawString(
|
||||
"" + this.distance_to_next_point + "m",
|
||||
0,
|
||||
g.getHeight() - 49
|
||||
);
|
||||
|
||||
let point_time = this.old_times[this.old_times.length - 1];
|
||||
let done_in = point_time - this.starting_time - this.paused_time;
|
||||
let approximate_speed = Math.round(
|
||||
(this.advanced_distance * 3.6) / done_in
|
||||
);
|
||||
let approximate_instant_speed = Math.round(this.instant_speed * 3.6);
|
||||
|
||||
g.setFont("6x8:2")
|
||||
.setFontAlign(-1, -1, 0)
|
||||
.drawString(
|
||||
"" + approximate_speed + "km/h (in." + approximate_instant_speed + ")",
|
||||
0,
|
||||
g.getHeight() - 15
|
||||
);
|
||||
|
||||
g.setFont("6x8:2").drawString(
|
||||
"" + rounded_distance + "/" + total,
|
||||
0,
|
||||
g.getHeight() - 32
|
||||
);
|
||||
|
||||
if (this.distance_to_next_point <= 100) {
|
||||
if (this.path.is_waypoint(this.reaching)) {
|
||||
g.setColor(0.0, 1.0, 0.0)
|
||||
.setFont("6x15")
|
||||
.drawString("turn", g.getWidth() - 50, 30);
|
||||
}
|
||||
}
|
||||
if (!this.on_path) {
|
||||
g.setColor(1.0, 0.0, 0.0)
|
||||
.setFont("6x15")
|
||||
.drawString("lost", g.getWidth() - 55, 35);
|
||||
}
|
||||
}
|
||||
display_map() {
|
||||
// don't display all segments, only those neighbouring current segment
|
||||
// this is most likely to be the correct display
|
||||
// while lowering the cost a lot
|
||||
//
|
||||
// note that all code is inlined here to speed things up from 400ms to 200ms
|
||||
let start = Math.max(this.current_segment - 4, 0);
|
||||
let end = Math.min(this.current_segment + 6, this.path.len);
|
||||
let pos = this.position;
|
||||
let cos = this.adjusted_cos_direction;
|
||||
let sin = this.adjusted_sin_direction;
|
||||
let points = this.path.points;
|
||||
let cx = pos.lon;
|
||||
let cy = pos.lat;
|
||||
let half_width = g.getWidth() / 2;
|
||||
let half_height = g.getHeight() / 2;
|
||||
let previous_x = null;
|
||||
let previous_y = null;
|
||||
for (let i = start; i < end; i++) {
|
||||
let tx = (points[2 * i] - cx) * 40000.0;
|
||||
let ty = (points[2 * i + 1] - cy) * 40000.0;
|
||||
let rotated_x = tx * cos - ty * sin;
|
||||
let rotated_y = tx * sin + ty * cos;
|
||||
let x = half_width - Math.round(rotated_x); // x is inverted
|
||||
let y = half_height + Math.round(rotated_y);
|
||||
if (previous_x !== null) {
|
||||
if (i == this.current_segment + 1) {
|
||||
g.setColor(0.0, 1.0, 0.0);
|
||||
} else {
|
||||
g.setColor(1.0, 0.0, 0.0);
|
||||
}
|
||||
g.drawLine(previous_x, previous_y, x, y);
|
||||
|
||||
if (this.path.is_waypoint(i - 1)) {
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillCircle(previous_x, previous_y, 6);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillCircle(previous_x, previous_y, 5);
|
||||
}
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillCircle(previous_x, previous_y, 4);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillCircle(previous_x, previous_y, 3);
|
||||
}
|
||||
|
||||
previous_x = x;
|
||||
previous_y = y;
|
||||
}
|
||||
|
||||
if (this.path.is_waypoint(end - 1)) {
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillCircle(previous_x, previous_y, 6);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillCircle(previous_x, previous_y, 5);
|
||||
}
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillCircle(previous_x, previous_y, 4);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillCircle(previous_x, previous_y, 3);
|
||||
|
||||
// now display ourselves
|
||||
g.setColor(g.theme.fgH);
|
||||
g.fillCircle(half_width, half_height, 5);
|
||||
|
||||
// display old points for direction debug
|
||||
// for (let i = 0; i < this.old_points.length; i++) {
|
||||
// let tx = (this.old_points[i].lon - cx) * 40000.0;
|
||||
// let ty = (this.old_points[i].lat - cy) * 40000.0;
|
||||
// let rotated_x = tx * cos - ty * sin;
|
||||
// let rotated_y = tx * sin + ty * cos;
|
||||
// let x = half_width - Math.round(rotated_x); // x is inverted
|
||||
// let y = half_height + Math.round(rotated_y);
|
||||
// g.setColor((i + 1) / 4.0, 0.0, 0.0);
|
||||
// g.fillCircle(x, y, 3);
|
||||
// }
|
||||
|
||||
// display current-segment's projection for debug
|
||||
let projection = pos.closest_segment_point(
|
||||
this.path.point(this.current_segment),
|
||||
this.path.point(this.current_segment + 1)
|
||||
);
|
||||
|
||||
let tx = (projection.lon - cx) * 40000.0;
|
||||
let ty = (projection.lat - cy) * 40000.0;
|
||||
let rotated_x = tx * cos - ty * sin;
|
||||
let rotated_y = tx * sin + ty * cos;
|
||||
let x = half_width - Math.round(rotated_x); // x is inverted
|
||||
let y = half_height + Math.round(rotated_y);
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillCircle(x, y, 4);
|
||||
|
||||
// display direction to next point if lost
|
||||
if (!this.on_path) {
|
||||
let next_point = this.path.point(this.current_segment + 1);
|
||||
let diff = next_point.minus(this.position);
|
||||
let angle = Math.atan2(diff.lat, diff.lon);
|
||||
let tx = Math.cos(angle) * 50.0;
|
||||
let ty = Math.sin(angle) * 50.0;
|
||||
let rotated_x = tx * cos - ty * sin;
|
||||
let rotated_y = tx * sin + ty * cos;
|
||||
let x = half_width - Math.round(rotated_x); // x is inverted
|
||||
let y = half_height + Math.round(rotated_y);
|
||||
g.setColor(g.theme.fgH).drawLine(half_width, half_height, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function load_gpc(filename) {
|
||||
let buffer = require("Storage").readArrayBuffer(filename);
|
||||
let offset = 0;
|
||||
|
||||
// header
|
||||
let header = Uint16Array(buffer, offset, 5);
|
||||
offset += 5 * 2;
|
||||
let key = header[0];
|
||||
let version = header[1];
|
||||
let points_number = header[2];
|
||||
if (key != code_key || version > file_version) {
|
||||
E.showMessage("Invalid gpc file");
|
||||
load();
|
||||
}
|
||||
|
||||
// path points
|
||||
let points = Float64Array(buffer, offset, points_number * 2);
|
||||
offset += 8 * points_number * 2;
|
||||
|
||||
// path waypoints
|
||||
let waypoints_len = Math.ceil(points_number / 8.0);
|
||||
let waypoints = Uint8Array(buffer, offset, waypoints_len);
|
||||
offset += waypoints_len;
|
||||
|
||||
// interest points
|
||||
let interests_number = header[3];
|
||||
let interests_coordinates = Float64Array(
|
||||
buffer,
|
||||
offset,
|
||||
interests_number * 2
|
||||
);
|
||||
offset += 8 * interests_number * 2;
|
||||
let interests_types = Uint8Array(buffer, offset, interests_number);
|
||||
offset += interests_number;
|
||||
|
||||
// interests on path
|
||||
let interests_on_path_number = header[4];
|
||||
let interests_on_path = Uint16Array(buffer, offset, interests_on_path_number);
|
||||
offset += 2 * interests_on_path_number;
|
||||
let starts_length = Math.ceil(interests_on_path_number / 5.0);
|
||||
let interests_starts = Uint16Array(buffer, offset, starts_length);
|
||||
offset += 2 * starts_length;
|
||||
|
||||
return [
|
||||
points,
|
||||
waypoints,
|
||||
interests_coordinates,
|
||||
interests_types,
|
||||
interests_on_path,
|
||||
interests_starts,
|
||||
];
|
||||
}
|
||||
|
||||
class Path {
|
||||
constructor(arrays) {
|
||||
this.points = arrays[0];
|
||||
this.waypoints = arrays[1];
|
||||
this.interests_coordinates = arrays[2];
|
||||
this.interests_types = arrays[3];
|
||||
this.interests_on_path = arrays[4];
|
||||
this.interests_starts = arrays[5];
|
||||
}
|
||||
|
||||
is_waypoint(point_index) {
|
||||
let i = Math.floor(point_index / 8);
|
||||
let subindex = point_index % 8;
|
||||
let r = this.waypoints[i] & (1 << subindex);
|
||||
return r != 0;
|
||||
}
|
||||
|
||||
// execute op on all segments.
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// return point at given index
|
||||
point(index) {
|
||||
let lon = this.points[2 * index];
|
||||
let lat = this.points[2 * index + 1];
|
||||
return new Point(lon, lat);
|
||||
}
|
||||
|
||||
interest_point(index) {
|
||||
let lon = this.interests_coordinates[2 * index];
|
||||
let lat = this.interests_coordinates[2 * index + 1];
|
||||
return new Point(lon, lat);
|
||||
}
|
||||
|
||||
interest_color(index) {
|
||||
return interests_colors[this.interests_types[index]];
|
||||
}
|
||||
|
||||
// return index of segment which is nearest from point.
|
||||
// we need a direction because we need there is an ambiguity
|
||||
// for overlapping segments which are taken once to go and once to come back.
|
||||
// (in the other direction).
|
||||
nearest_segment(point, start, end, cos_direction, sin_direction) {
|
||||
// we are going to compute two min distances, one for each direction.
|
||||
let indices = [0, 0];
|
||||
let mins = [Number.MAX_VALUE, Number.MAX_VALUE];
|
||||
this.on_segments(
|
||||
function (p1, p2, i) {
|
||||
// we use the dot product to figure out if oriented correctly
|
||||
// let distance = point.fake_distance_to_segment(p1, p2);
|
||||
|
||||
let projection = point.closest_segment_point(p1, p2);
|
||||
let distance = point.fake_distance(projection);
|
||||
|
||||
// let d = projection.minus(point).times(40000.0);
|
||||
// let rotated_x = d.lon * acos - d.lat * asin;
|
||||
// let rotated_y = d.lon * asin + d.lat * acos;
|
||||
// let x = g.getWidth() / 2 - Math.round(rotated_x); // x is inverted
|
||||
// let y = g.getHeight() / 2 + Math.round(rotated_y);
|
||||
//
|
||||
let diff = p2.minus(p1);
|
||||
let dot = cos_direction * diff.lon + sin_direction * diff.lat;
|
||||
let orientation = +(dot < 0); // index 0 is good orientation
|
||||
// g.setColor(0.0, 0.0 + orientation, 1.0 - orientation).fillCircle(
|
||||
// x,
|
||||
// y,
|
||||
// 10
|
||||
// );
|
||||
if (distance <= mins[orientation]) {
|
||||
mins[orientation] = distance;
|
||||
indices[orientation] = i - 1;
|
||||
}
|
||||
},
|
||||
start,
|
||||
end
|
||||
);
|
||||
// by default correct orientation (0) wins
|
||||
// but if other one is really closer, return other one
|
||||
if (mins[1] < mins[0] / 10.0) {
|
||||
return [1, indices[1]];
|
||||
} else {
|
||||
return [0, indices[0]];
|
||||
}
|
||||
}
|
||||
get len() {
|
||||
return this.points.length / 2;
|
||||
}
|
||||
}
|
||||
|
||||
class Point {
|
||||
constructor(lon, lat) {
|
||||
this.lon = lon;
|
||||
this.lat = lat;
|
||||
}
|
||||
coordinates(current_position, cos_direction, sin_direction) {
|
||||
let translated = this.minus(current_position).times(40000.0);
|
||||
let rotated_x =
|
||||
translated.lon * cos_direction - translated.lat * sin_direction;
|
||||
let rotated_y =
|
||||
translated.lon * sin_direction + translated.lat * cos_direction;
|
||||
return [
|
||||
g.getWidth() / 2 - Math.round(rotated_x), // x is inverted
|
||||
g.getHeight() / 2 + Math.round(rotated_y),
|
||||
];
|
||||
}
|
||||
minus(other_point) {
|
||||
let xdiff = this.lon - other_point.lon;
|
||||
let ydiff = this.lat - other_point.lat;
|
||||
return new Point(xdiff, ydiff);
|
||||
}
|
||||
plus(other_point) {
|
||||
return new Point(this.lon + other_point.lon, this.lat + other_point.lat);
|
||||
}
|
||||
length_squared(other_point) {
|
||||
let d = this.minus(other_point);
|
||||
return d.lon * d.lon + d.lat * d.lat;
|
||||
}
|
||||
times(scalar) {
|
||||
return new Point(this.lon * scalar, this.lat * scalar);
|
||||
}
|
||||
dot(other_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 meters
|
||||
}
|
||||
fake_distance(other_point) {
|
||||
return Math.sqrt(this.length_squared(other_point));
|
||||
}
|
||||
closest_segment_point(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
|
||||
if (l2 == 0.0) {
|
||||
return 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.
|
||||
// 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));
|
||||
return v.plus(w.minus(v).times(t)); // Projection falls on the segment
|
||||
}
|
||||
distance_to_segment(v, w) {
|
||||
let projection = this.closest_segment_point(v, w);
|
||||
return this.distance(projection);
|
||||
}
|
||||
fake_distance_to_segment(v, w) {
|
||||
let projection = this.closest_segment_point(v, w);
|
||||
return this.fake_distance(projection);
|
||||
}
|
||||
}
|
||||
|
||||
Bangle.loadWidgets();
|
||||
|
||||
let fake_gps_point = 0.0;
|
||||
function simulate_gps(status) {
|
||||
if (fake_gps_point > status.path.len - 1) {
|
||||
return;
|
||||
}
|
||||
let point_index = Math.floor(fake_gps_point);
|
||||
if (point_index >= status.path.len) {
|
||||
return;
|
||||
}
|
||||
//let p1 = status.path.point(0);
|
||||
//let n = status.path.len;
|
||||
//let p2 = status.path.point(n - 1);
|
||||
let p1 = status.path.point(point_index);
|
||||
let p2 = status.path.point(point_index + 1);
|
||||
|
||||
let alpha = fake_gps_point - point_index;
|
||||
let pos = p1.times(1 - alpha).plus(p2.times(alpha));
|
||||
let old_pos = status.position;
|
||||
|
||||
fake_gps_point += 0.05; // advance simulation
|
||||
status.update_position(pos, null);
|
||||
}
|
||||
|
||||
function drawMenu() {
|
||||
const menu = {
|
||||
"": { title: "choose trace" },
|
||||
};
|
||||
var files = require("Storage").list(".gpc");
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
menu[files[i]] = start.bind(null, files[i]);
|
||||
}
|
||||
menu["Exit"] = function () {
|
||||
load();
|
||||
};
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function start(fn) {
|
||||
E.showMenu();
|
||||
console.log("loading", fn);
|
||||
|
||||
// let path = new Path(load_gpx("test.gpx"));
|
||||
let path = new Path(load_gpc(fn));
|
||||
let status = new Status(path);
|
||||
|
||||
if (simulated) {
|
||||
status.position = new Point(status.path.point(0));
|
||||
setInterval(simulate_gps, 500, status);
|
||||
} else {
|
||||
// let's display start while waiting for gps signal
|
||||
let p1 = status.path.point(0);
|
||||
let p2 = status.path.point(1);
|
||||
let diff = p2.minus(p1);
|
||||
let direction = Math.atan2(diff.lat, diff.lon);
|
||||
Bangle.setLocked(false);
|
||||
status.update_position(p1, direction);
|
||||
|
||||
let frame = 0;
|
||||
let set_coordinates = function (data) {
|
||||
frame += 1;
|
||||
// 0,0 coordinates are considered invalid since we sometimes receive them out of nowhere
|
||||
let valid_coordinates =
|
||||
!isNaN(data.lat) &&
|
||||
!isNaN(data.lon) &&
|
||||
(data.lat != 0.0 || data.lon != 0.0);
|
||||
if (valid_coordinates) {
|
||||
status.update_position(new Point(data.lon, data.lat), null);
|
||||
}
|
||||
let gps_status_color;
|
||||
if (frame % 2 == 0 || valid_coordinates) {
|
||||
gps_status_color = g.theme.bg;
|
||||
} else {
|
||||
gps_status_color = g.theme.fg;
|
||||
}
|
||||
g.setColor(gps_status_color)
|
||||
.setFont("6x8:2")
|
||||
.drawString("gps", g.getWidth() - 40, 30);
|
||||
};
|
||||
|
||||
Bangle.setGPSPower(true, "gipy");
|
||||
Bangle.on("GPS", set_coordinates);
|
||||
Bangle.on("lock", function (on) {
|
||||
if (!on) {
|
||||
Bangle.setGPSPower(true, "gipy"); // activate gps when unlocking
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let files = require("Storage").list(".gpc");
|
||||
if (files.length <= 1) {
|
||||
if (files.length == 0) {
|
||||
load();
|
||||
} else {
|
||||
start(files[0]);
|
||||
}
|
||||
} else {
|
||||
drawMenu();
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,196 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
<style>
|
||||
svg { width:95% }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Please select a gpx file to be converted to gpc and loaded.</p>
|
||||
|
||||
|
||||
gpx file : <input type="file" is="gpx_file" id="fileInput" accept=".gpx">
|
||||
<br>
|
||||
gpc filename : <input type="text" id="gpc_file" name="gpc_file" maxlength="24">.gpc (max 24 characters)
|
||||
<br>
|
||||
<input type="checkbox" id="osm" name="osm" checked>
|
||||
<label for="osm">fetch interests from openstreetmap</label>
|
||||
<table>
|
||||
<tr>
|
||||
<th><bold>OpenstreetMap <a href="https://wiki.openstreetmap.org/wiki/Tags">NODE Tags</a></bold></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>color</th><th>key</th><th>value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="color:red">red</th><th><input type="text" id="key1" name="key1" value="shop"></th><th><input type="text" id="value1" name="value1" value="bakery"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="color:blue">blue</th><th><input type="text" id="key2" name="key2" value="amenity"></th><th><input type="text" id="value2" name="value2" value="drinking_water"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="color:cyan">cyan</th><th><input type="text" id="key3" name="key3" value="amenity"></th><th><input type="text" id="value3" name="value3" value="toilets"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="color:green">green</th><th><input type="text" id="key4" name="key4" value="tourism"></th><th><input type="text" id="value4" name="value4" value="artwork"></th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>nice tags could be :
|
||||
shop/bicycle, amenity/bank, shop/supermarket, leisure/picnic_table, tourism/information, amenity/pharmacy
|
||||
</p>
|
||||
|
||||
<input type="button" id="convert" name="convert" value="Convert" disabled>
|
||||
|
||||
|
||||
<input type="button" id="upload" name="upload" value="Upload" disabled>
|
||||
<div id="status"></div>
|
||||
<div id="map"></div>
|
||||
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
<script>
|
||||
function onInit() {
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script type="module">
|
||||
|
||||
function vec_to_string(vec) {
|
||||
let final_string = '';
|
||||
for (let i = 0 ; i < vec.length ; i++) {
|
||||
final_string += String.fromCharCode(vec[i]);
|
||||
}
|
||||
return final_string;
|
||||
}
|
||||
|
||||
import init, { convert_gpx_strings, convert_gpx_strings_no_osm, get_gpc, get_svg } from "./pkg/gpconv.js";
|
||||
console.log("imported wasm");
|
||||
|
||||
let osm_checkbox = document.querySelector("input[name=osm]");
|
||||
let with_osm = true;
|
||||
|
||||
osm_checkbox.addEventListener('change', function() {
|
||||
if (this.checked) {
|
||||
with_osm = true;
|
||||
document.getElementById('key1').disabled = false;
|
||||
document.getElementById('key2').disabled = false;
|
||||
document.getElementById('key3').disabled = false;
|
||||
document.getElementById('key4').disabled = false;
|
||||
document.getElementById('value1').disabled = false;
|
||||
document.getElementById('value2').disabled = false;
|
||||
document.getElementById('value3').disabled = false;
|
||||
document.getElementById('value4').disabled = false;
|
||||
} else {
|
||||
with_osm = false;
|
||||
document.getElementById('key1').disabled = true;
|
||||
document.getElementById('key2').disabled = true;
|
||||
document.getElementById('key3').disabled = true;
|
||||
document.getElementById('key4').disabled = true;
|
||||
document.getElementById('value1').disabled = true;
|
||||
document.getElementById('value2').disabled = true;
|
||||
document.getElementById('value3').disabled = true;
|
||||
document.getElementById('value4').disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
let status = document.getElementById("status");
|
||||
let gpx_content = null;
|
||||
let gpc_filename = null;
|
||||
let gpc_content = null;
|
||||
|
||||
document
|
||||
.getElementById("fileInput")
|
||||
.addEventListener("change", function selectedFileChanged() {
|
||||
document.getElementById('convert').disabled = true;
|
||||
document.getElementById('upload').disabled = true;
|
||||
if (this.files.length === 0) {
|
||||
console.log("No file selected.");
|
||||
return;
|
||||
}
|
||||
status.innerHTML = "reading file";
|
||||
|
||||
let gpx_filename = this.files[0].name;
|
||||
if (gpc_filename === null || gpc_filename == "") {
|
||||
if (gpx_filename.length <= 28) {
|
||||
gpc_filename = gpx_filename.slice(0, gpx_filename.length - 4);
|
||||
document.getElementById('gpc_file').value = gpc_filename;
|
||||
}
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function fileReadCompleted() {
|
||||
console.log("reading file completed");
|
||||
status.innerHTML = "file reading completed";
|
||||
gpx_content = reader.result;
|
||||
document.getElementById('convert').disabled = false;
|
||||
};
|
||||
reader.readAsText(this.files[0]);
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById("convert")
|
||||
.addEventListener('click', function() {
|
||||
console.log("starting conversion");
|
||||
document.getElementById('convert').disabled = true;
|
||||
document.getElementById('upload').disabled = true;
|
||||
status.innerHTML = "please wait, converting file";
|
||||
init().then(() => {
|
||||
let gpc_svg;
|
||||
if (with_osm) {
|
||||
let key1 = document.getElementById('key1').value;
|
||||
let key2 = document.getElementById('key2').value;
|
||||
let key3 = document.getElementById('key3').value;
|
||||
let key4 = document.getElementById('key4').value;
|
||||
let value1 = document.getElementById('value1').value;
|
||||
let value2 = document.getElementById('value2').value;
|
||||
let value3 = document.getElementById('value3').value;
|
||||
let value4 = document.getElementById('value4').value;
|
||||
gpc_svg = convert_gpx_strings(gpx_content, key1, value1, key2, value2, key3, value3, key4, value4);
|
||||
} else {
|
||||
gpc_svg = convert_gpx_strings_no_osm(gpx_content);
|
||||
}
|
||||
gpc_svg.then(gs => {
|
||||
status.innerHTML = "file converted";
|
||||
let svg = get_svg(gs);
|
||||
let svg_string = vec_to_string(svg);
|
||||
let img = document.getElementById("map");
|
||||
img.innerHTML = svg_string;
|
||||
gpc_content = get_gpc(gs);
|
||||
if (gpc_filename !== null) {
|
||||
document.getElementById('upload').disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById("gpc_file")
|
||||
.addEventListener('change', function() {
|
||||
gpc_filename = document.getElementById("gpc_file").value;
|
||||
if (gpc_filename == "") {
|
||||
document.getElementById("upload").disabled = true;
|
||||
} else {
|
||||
if (gpc_content !== null) {
|
||||
document.getElementById("upload").disabled = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
document
|
||||
.getElementById("upload")
|
||||
.addEventListener('click', function() {
|
||||
status.innerHTML = "uploading file";
|
||||
console.log("uploading");
|
||||
let gpc_string = vec_to_string(gpc_content);
|
||||
Util.writeStorage(gpc_filename + ".gpc", gpc_string, () => {
|
||||
status.innerHTML = `${gpc_filename}.gpc uploaded`;
|
||||
console.log("DONE");
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"id": "gipy",
|
||||
"name": "Gipy",
|
||||
"shortName": "Gipy",
|
||||
"version": "0.15",
|
||||
"description": "Follow gpx files",
|
||||
"allow_emulator":false,
|
||||
"icon": "gipy.png",
|
||||
"type": "app",
|
||||
"tags": "tool,outdoors,gps",
|
||||
"screenshots": [],
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"interface": "interface.html",
|
||||
"storage": [
|
||||
{"name":"gipy.app.js","url":"app.js"},
|
||||
{"name":"gipy.settings.js","url":"settings.js"},
|
||||
{"name":"gipy.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [
|
||||
{"name":"gipy.json"}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* @param {GpcSvg} gpcsvg
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function get_gpc(gpcsvg: GpcSvg): Uint8Array;
|
||||
/**
|
||||
* @param {GpcSvg} gpcsvg
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function get_svg(gpcsvg: GpcSvg): Uint8Array;
|
||||
/**
|
||||
* @param {string} input_str
|
||||
* @returns {Promise<GpcSvg>}
|
||||
*/
|
||||
export function convert_gpx_strings_no_osm(input_str: string): Promise<GpcSvg>;
|
||||
/**
|
||||
* @param {string} input_str
|
||||
* @param {string} key1
|
||||
* @param {string} value1
|
||||
* @param {string} key2
|
||||
* @param {string} value2
|
||||
* @param {string} key3
|
||||
* @param {string} value3
|
||||
* @param {string} key4
|
||||
* @param {string} value4
|
||||
* @returns {Promise<GpcSvg>}
|
||||
*/
|
||||
export function convert_gpx_strings(input_str: string, key1: string, value1: string, key2: string, value2: string, key3: string, value3: string, key4: string, value4: string): Promise<GpcSvg>;
|
||||
/**
|
||||
*/
|
||||
export class GpcSvg {
|
||||
free(): void;
|
||||
}
|
||||
|
||||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
export interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly __wbg_gpcsvg_free: (a: number) => void;
|
||||
readonly get_gpc: (a: number, b: number) => void;
|
||||
readonly get_svg: (a: number, b: number) => void;
|
||||
readonly convert_gpx_strings_no_osm: (a: number, b: number) => number;
|
||||
readonly convert_gpx_strings: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number, q: number, r: number) => number;
|
||||
readonly __wbindgen_malloc: (a: number) => number;
|
||||
readonly __wbindgen_realloc: (a: number, b: number, c: number) => number;
|
||||
readonly __wbindgen_export_2: WebAssembly.Table;
|
||||
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0601691a32604cdd: (a: number, b: number, c: number) => void;
|
||||
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
||||
readonly __wbindgen_free: (a: number, b: number) => void;
|
||||
readonly __wbindgen_exn_store: (a: number) => void;
|
||||
readonly wasm_bindgen__convert__closures__invoke2_mut__h25ed812378167476: (a: number, b: number, c: number, d: number) => void;
|
||||
}
|
||||
|
||||
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
||||
/**
|
||||
* Instantiates the given `module`, which can either be bytes or
|
||||
* a precompiled `WebAssembly.Module`.
|
||||
*
|
||||
* @param {SyncInitInput} module
|
||||
*
|
||||
* @returns {InitOutput}
|
||||
*/
|
||||
export function initSync(module: SyncInitInput): InitOutput;
|
||||
|
||||
/**
|
||||
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
||||
* for everything else, calls `WebAssembly.instantiate` directly.
|
||||
*
|
||||
* @param {InitInput | Promise<InitInput>} module_or_path
|
||||
*
|
||||
* @returns {Promise<InitOutput>}
|
||||
*/
|
||||
export default function init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;
|
|
@ -0,0 +1,645 @@
|
|||
|
||||
let wasm;
|
||||
|
||||
const heap = new Array(32).fill(undefined);
|
||||
|
||||
heap.push(undefined, null, true, false);
|
||||
|
||||
function getObject(idx) { return heap[idx]; }
|
||||
|
||||
let heap_next = heap.length;
|
||||
|
||||
function dropObject(idx) {
|
||||
if (idx < 36) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
let cachedUint8Memory0 = new Uint8Array();
|
||||
|
||||
function getUint8Memory0() {
|
||||
if (cachedUint8Memory0.byteLength === 0) {
|
||||
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint8Memory0;
|
||||
}
|
||||
|
||||
const cachedTextEncoder = new TextEncoder('utf-8');
|
||||
|
||||
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||
? function (arg, view) {
|
||||
return cachedTextEncoder.encodeInto(arg, view);
|
||||
}
|
||||
: function (arg, view) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length
|
||||
};
|
||||
});
|
||||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length);
|
||||
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len);
|
||||
|
||||
const mem = getUint8Memory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3);
|
||||
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let cachedInt32Memory0 = new Int32Array();
|
||||
|
||||
function getInt32Memory0() {
|
||||
if (cachedInt32Memory0.byteLength === 0) {
|
||||
cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedInt32Memory0;
|
||||
}
|
||||
|
||||
const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
function debugString(val) {
|
||||
// primitive types
|
||||
const type = typeof val;
|
||||
if (type == 'number' || type == 'boolean' || val == null) {
|
||||
return `${val}`;
|
||||
}
|
||||
if (type == 'string') {
|
||||
return `"${val}"`;
|
||||
}
|
||||
if (type == 'symbol') {
|
||||
const description = val.description;
|
||||
if (description == null) {
|
||||
return 'Symbol';
|
||||
} else {
|
||||
return `Symbol(${description})`;
|
||||
}
|
||||
}
|
||||
if (type == 'function') {
|
||||
const name = val.name;
|
||||
if (typeof name == 'string' && name.length > 0) {
|
||||
return `Function(${name})`;
|
||||
} else {
|
||||
return 'Function';
|
||||
}
|
||||
}
|
||||
// objects
|
||||
if (Array.isArray(val)) {
|
||||
const length = val.length;
|
||||
let debug = '[';
|
||||
if (length > 0) {
|
||||
debug += debugString(val[0]);
|
||||
}
|
||||
for(let i = 1; i < length; i++) {
|
||||
debug += ', ' + debugString(val[i]);
|
||||
}
|
||||
debug += ']';
|
||||
return debug;
|
||||
}
|
||||
// Test for built-in
|
||||
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
|
||||
let className;
|
||||
if (builtInMatches.length > 1) {
|
||||
className = builtInMatches[1];
|
||||
} else {
|
||||
// Failed to match the standard '[object ClassName]'
|
||||
return toString.call(val);
|
||||
}
|
||||
if (className == 'Object') {
|
||||
// we're a user defined class or Object
|
||||
// JSON.stringify avoids problems with cycles, and is generally much
|
||||
// easier than looping through ownProperties of `val`.
|
||||
try {
|
||||
return 'Object(' + JSON.stringify(val) + ')';
|
||||
} catch (_) {
|
||||
return 'Object';
|
||||
}
|
||||
}
|
||||
// errors
|
||||
if (val instanceof Error) {
|
||||
return `${val.name}: ${val.message}\n${val.stack}`;
|
||||
}
|
||||
// TODO we could test for more things here, like `Set`s and `Map`s.
|
||||
return className;
|
||||
}
|
||||
|
||||
function makeMutClosure(arg0, arg1, dtor, f) {
|
||||
const state = { a: arg0, b: arg1, cnt: 1, dtor };
|
||||
const real = (...args) => {
|
||||
// First up with a closure we increment the internal reference
|
||||
// count. This ensures that the Rust closure environment won't
|
||||
// be deallocated while we're invoking it.
|
||||
state.cnt++;
|
||||
const a = state.a;
|
||||
state.a = 0;
|
||||
try {
|
||||
return f(a, state.b, ...args);
|
||||
} finally {
|
||||
if (--state.cnt === 0) {
|
||||
wasm.__wbindgen_export_2.get(state.dtor)(a, state.b);
|
||||
|
||||
} else {
|
||||
state.a = a;
|
||||
}
|
||||
}
|
||||
};
|
||||
real.original = state;
|
||||
|
||||
return real;
|
||||
}
|
||||
function __wbg_adapter_24(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0601691a32604cdd(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function _assertClass(instance, klass) {
|
||||
if (!(instance instanceof klass)) {
|
||||
throw new Error(`expected instance of ${klass.name}`);
|
||||
}
|
||||
return instance.ptr;
|
||||
}
|
||||
|
||||
function getArrayU8FromWasm0(ptr, len) {
|
||||
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
|
||||
}
|
||||
/**
|
||||
* @param {GpcSvg} gpcsvg
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function get_gpc(gpcsvg) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
_assertClass(gpcsvg, GpcSvg);
|
||||
wasm.get_gpc(retptr, gpcsvg.ptr);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
var v0 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_free(r0, r1 * 1);
|
||||
return v0;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GpcSvg} gpcsvg
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function get_svg(gpcsvg) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
_assertClass(gpcsvg, GpcSvg);
|
||||
wasm.get_svg(retptr, gpcsvg.ptr);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
var v0 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_free(r0, r1 * 1);
|
||||
return v0;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input_str
|
||||
* @returns {Promise<GpcSvg>}
|
||||
*/
|
||||
export function convert_gpx_strings_no_osm(input_str) {
|
||||
const ptr0 = passStringToWasm0(input_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.convert_gpx_strings_no_osm(ptr0, len0);
|
||||
return takeObject(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input_str
|
||||
* @param {string} key1
|
||||
* @param {string} value1
|
||||
* @param {string} key2
|
||||
* @param {string} value2
|
||||
* @param {string} key3
|
||||
* @param {string} value3
|
||||
* @param {string} key4
|
||||
* @param {string} value4
|
||||
* @returns {Promise<GpcSvg>}
|
||||
*/
|
||||
export function convert_gpx_strings(input_str, key1, value1, key2, value2, key3, value3, key4, value4) {
|
||||
const ptr0 = passStringToWasm0(input_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passStringToWasm0(key1, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ptr2 = passStringToWasm0(value1, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len2 = WASM_VECTOR_LEN;
|
||||
const ptr3 = passStringToWasm0(key2, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len3 = WASM_VECTOR_LEN;
|
||||
const ptr4 = passStringToWasm0(value2, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len4 = WASM_VECTOR_LEN;
|
||||
const ptr5 = passStringToWasm0(key3, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len5 = WASM_VECTOR_LEN;
|
||||
const ptr6 = passStringToWasm0(value3, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len6 = WASM_VECTOR_LEN;
|
||||
const ptr7 = passStringToWasm0(key4, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len7 = WASM_VECTOR_LEN;
|
||||
const ptr8 = passStringToWasm0(value4, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len8 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.convert_gpx_strings(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, ptr8, len8);
|
||||
return takeObject(ret);
|
||||
}
|
||||
|
||||
function handleError(f, args) {
|
||||
try {
|
||||
return f.apply(this, args);
|
||||
} catch (e) {
|
||||
wasm.__wbindgen_exn_store(addHeapObject(e));
|
||||
}
|
||||
}
|
||||
function __wbg_adapter_69(arg0, arg1, arg2, arg3) {
|
||||
wasm.wasm_bindgen__convert__closures__invoke2_mut__h25ed812378167476(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
export class GpcSvg {
|
||||
|
||||
static __wrap(ptr) {
|
||||
const obj = Object.create(GpcSvg.prototype);
|
||||
obj.ptr = ptr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_gpcsvg_free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
async function load(module, imports) {
|
||||
if (typeof Response === 'function' && module instanceof Response) {
|
||||
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
||||
try {
|
||||
return await WebAssembly.instantiateStreaming(module, imports);
|
||||
|
||||
} catch (e) {
|
||||
if (module.headers.get('Content-Type') != 'application/wasm') {
|
||||
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
|
||||
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bytes = await module.arrayBuffer();
|
||||
return await WebAssembly.instantiate(bytes, imports);
|
||||
|
||||
} else {
|
||||
const instance = await WebAssembly.instantiate(module, imports);
|
||||
|
||||
if (instance instanceof WebAssembly.Instance) {
|
||||
return { instance, module };
|
||||
|
||||
} else {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getImports() {
|
||||
const imports = {};
|
||||
imports.wbg = {};
|
||||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
imports.wbg.__wbg_gpcsvg_new = function(arg0) {
|
||||
const ret = GpcSvg.__wrap(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
|
||||
const ret = getObject(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_fetch_386f87a3ebf5003c = function(arg0) {
|
||||
const ret = fetch(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||
const obj = takeObject(arg0).original;
|
||||
if (obj.cnt-- == 1) {
|
||||
obj.a = 0;
|
||||
return true;
|
||||
}
|
||||
const ret = false;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_fetch_749a56934f95c96c = function(arg0, arg1) {
|
||||
const ret = getObject(arg0).fetch(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_Response_eaa426220848a39e = function(arg0) {
|
||||
let result;
|
||||
try {
|
||||
result = getObject(arg0) instanceof Response;
|
||||
} catch {
|
||||
result = false;
|
||||
}
|
||||
const ret = result;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_url_74285ddf2747cb3d = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).url;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_status_c4ef3dd591e63435 = function(arg0) {
|
||||
const ret = getObject(arg0).status;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_headers_fd64ad685cf22e5d = function(arg0) {
|
||||
const ret = getObject(arg0).headers;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_text_1169d752cc697903 = function() { return handleError(function (arg0) {
|
||||
const ret = getObject(arg0).text();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_newwithstrandinit_05d7180788420c40 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_new_2d0053ee81e4dd2a = function() { return handleError(function () {
|
||||
const ret = new Headers();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_append_de37df908812970d = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).append(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_is_object = function(arg0) {
|
||||
const val = getObject(arg0);
|
||||
const ret = typeof(val) === 'object' && val !== null;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_newnoargs_b5b063fc6c2f0376 = function(arg0, arg1) {
|
||||
const ret = new Function(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_next_579e583d33566a86 = function(arg0) {
|
||||
const ret = getObject(arg0).next;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_is_function = function(arg0) {
|
||||
const ret = typeof(getObject(arg0)) === 'function';
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_value_1ccc36bc03462d71 = function(arg0) {
|
||||
const ret = getObject(arg0).value;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_iterator_6f9d4f28845f426c = function() {
|
||||
const ret = Symbol.iterator;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_0b9bfdd97583284e = function() {
|
||||
const ret = new Object();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_self_6d479506f72c6a71 = function() { return handleError(function () {
|
||||
const ret = self.self;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_window_f2557cc78490aceb = function() { return handleError(function () {
|
||||
const ret = window.window;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_globalThis_7f206bda628d5286 = function() { return handleError(function () {
|
||||
const ret = globalThis.globalThis;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_global_ba75c50d1cf384f4 = function() { return handleError(function () {
|
||||
const ret = global.global;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||
const ret = getObject(arg0) === undefined;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_call_97ae9d8645dc388b = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).call(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_call_168da88779e35f61 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_next_aaef7c8aa5e212ac = function() { return handleError(function (arg0) {
|
||||
const ret = getObject(arg0).next();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_done_1b73b0672e15f234 = function(arg0) {
|
||||
const ret = getObject(arg0).done;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_new_9962f939219f1820 = function(arg0, arg1) {
|
||||
try {
|
||||
var state0 = {a: arg0, b: arg1};
|
||||
var cb0 = (arg0, arg1) => {
|
||||
const a = state0.a;
|
||||
state0.a = 0;
|
||||
try {
|
||||
return __wbg_adapter_69(a, state0.b, arg0, arg1);
|
||||
} finally {
|
||||
state0.a = a;
|
||||
}
|
||||
};
|
||||
const ret = new Promise(cb0);
|
||||
return addHeapObject(ret);
|
||||
} finally {
|
||||
state0.a = state0.b = 0;
|
||||
}
|
||||
};
|
||||
imports.wbg.__wbg_resolve_99fe17964f31ffc0 = function(arg0) {
|
||||
const ret = Promise.resolve(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_then_11f7a54d67b4bfad = function(arg0, arg1) {
|
||||
const ret = getObject(arg0).then(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_then_cedad20fbbd9418a = function(arg0, arg1, arg2) {
|
||||
const ret = getObject(arg0).then(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_buffer_3f3d764d4747d564 = function(arg0) {
|
||||
const ret = getObject(arg0).buffer;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_newwithbyteoffsetandlength_d9aa266703cb98be = function(arg0, arg1, arg2) {
|
||||
const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_8c3f0052272a457a = function(arg0) {
|
||||
const ret = new Uint8Array(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_stringify_d6471d300ded9b68 = function() { return handleError(function (arg0) {
|
||||
const ret = JSON.stringify(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_765201544a2b6869 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = Reflect.get(getObject(arg0), getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_has_8359f114ce042f5a = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = Reflect.has(getObject(arg0), getObject(arg1));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_set_bf3f89b92d5a34bf = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||
const ret = debugString(getObject(arg1));
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
imports.wbg.__wbindgen_memory = function() {
|
||||
const ret = wasm.memory;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper947 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 147, __wbg_adapter_24);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
function initMemory(imports, maybe_memory) {
|
||||
|
||||
}
|
||||
|
||||
function finalizeInit(instance, module) {
|
||||
wasm = instance.exports;
|
||||
init.__wbindgen_wasm_module = module;
|
||||
cachedInt32Memory0 = new Int32Array();
|
||||
cachedUint8Memory0 = new Uint8Array();
|
||||
|
||||
|
||||
return wasm;
|
||||
}
|
||||
|
||||
function initSync(module) {
|
||||
const imports = getImports();
|
||||
|
||||
initMemory(imports);
|
||||
|
||||
if (!(module instanceof WebAssembly.Module)) {
|
||||
module = new WebAssembly.Module(module);
|
||||
}
|
||||
|
||||
const instance = new WebAssembly.Instance(module, imports);
|
||||
|
||||
return finalizeInit(instance, module);
|
||||
}
|
||||
|
||||
async function init(input) {
|
||||
if (typeof input === 'undefined') {
|
||||
input = new URL('gpconv_bg.wasm', import.meta.url);
|
||||
}
|
||||
const imports = getImports();
|
||||
|
||||
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
|
||||
input = fetch(input);
|
||||
}
|
||||
|
||||
initMemory(imports);
|
||||
|
||||
const { instance, module } = await load(await input, imports);
|
||||
|
||||
return finalizeInit(instance, module);
|
||||
}
|
||||
|
||||
export { initSync }
|
||||
export default init;
|
Binary file not shown.
|
@ -0,0 +1,16 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export function __wbg_gpcsvg_free(a: number): void;
|
||||
export function get_gpc(a: number, b: number): void;
|
||||
export function get_svg(a: number, b: number): void;
|
||||
export function convert_gpx_strings_no_osm(a: number, b: number): number;
|
||||
export function convert_gpx_strings(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number, q: number, r: number): number;
|
||||
export function __wbindgen_malloc(a: number): number;
|
||||
export function __wbindgen_realloc(a: number, b: number, c: number): number;
|
||||
export const __wbindgen_export_2: WebAssembly.Table;
|
||||
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0601691a32604cdd(a: number, b: number, c: number): void;
|
||||
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
||||
export function __wbindgen_free(a: number, b: number): void;
|
||||
export function __wbindgen_exn_store(a: number): void;
|
||||
export function wasm_bindgen__convert__closures__invoke2_mut__h25ed812378167476(a: number, b: number, c: number, d: number): void;
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "gpconv",
|
||||
"version": "0.1.0",
|
||||
"files": [
|
||||
"gpconv_bg.wasm",
|
||||
"gpconv.js",
|
||||
"gpconv.d.ts"
|
||||
],
|
||||
"module": "gpconv.js",
|
||||
"types": "gpconv.d.ts",
|
||||
"sideEffects": false
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
|
@ -0,0 +1,38 @@
|
|||
(function (back) {
|
||||
var FILE = "gipy.json";
|
||||
// Load settings
|
||||
var settings = Object.assign(
|
||||
{
|
||||
keep_gps_alive: false,
|
||||
max_speed: 35,
|
||||
},
|
||||
require("Storage").readJSON(FILE, true) || {}
|
||||
);
|
||||
|
||||
function writeSettings() {
|
||||
require("Storage").writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
// Show the menu
|
||||
E.showMenu({
|
||||
"": { title: "Gipy" },
|
||||
"< Back": () => back(),
|
||||
"keep gps alive": {
|
||||
value: !!settings.keep_gps_alive, // !! converts undefined to false
|
||||
format: (v) => (v ? "Yes" : "No"),
|
||||
onchange: (v) => {
|
||||
settings.keep_gps_alive = v;
|
||||
writeSettings();
|
||||
},
|
||||
},
|
||||
"max speed": {
|
||||
value: 35 | settings.max_speed, // 0| converts undefined to 0
|
||||
min: 0,
|
||||
max: 130,
|
||||
onchange: (v) => {
|
||||
settings.max_speed = v;
|
||||
writeSettings();
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
|
@ -15,5 +15,9 @@
|
|||
"strict": true,
|
||||
"typeRoots": ["./typescript/types"]
|
||||
},
|
||||
"include": ["./**/*"]
|
||||
"include": ["./**/*"],
|
||||
"exclude": [
|
||||
"**/gpconv.d.ts",
|
||||
"**/gpconv_bg.wasm.d.ts"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue