mirror of https://github.com/espruino/BangleApps
parent
caa5629ae3
commit
e3cc8717e6
|
@ -0,0 +1 @@
|
|||
0.01: New App! Very limited course support.
|
|
@ -0,0 +1,21 @@
|
|||
# App Name
|
||||
|
||||
Describe the app...
|
||||
|
||||
Add screen shots (if possible) to the app folder and link then into this file with 
|
||||
|
||||
## Usage
|
||||
|
||||
Select your course of interest upon loading this app.
|
||||
|
||||
## Contributions
|
||||
|
||||
The performance of this app depends on the accuracy and consistency of user-submitted maps. Please contribute to Open Street Map using these guidelines and provide input in ways to support this application.
|
||||
|
||||
## Controls
|
||||
|
||||
Swipe to change holes and tap to see a green closeup.
|
||||
|
||||
## Requests/Creator
|
||||
|
||||
[Jason Dekarske](https://github.com/jdekarske)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("AAkCpMkyQCBwANGggLCAQYOOpAONpMgBwgLFHxAOJyVABwt/8QRGBwsv/mREAwOEkv/K4IOFyBZE3/ypMiHwwODy/+AYOJPowFD//Jkh9HAodf/IsGAQMSAoVL/4FD7dtBwWCCgd/6QOE2wjGl/6AoVbBwJDBBwh6BGQYOB7ZBG3/pAoUtBwNkBwuX/ojDBwNsZw3/0gFCBwJcDPQn0AoYOB2QOFpf+AoZcCNYx6ELgRrGAQoOB7IONPQUJBxB6CAoNBBxB6CRINIBYdJlIOIggODkiJFU4UABwmSAoWWU4cAfwQOCpSJDBwcCBAOJHQRNDBwUggAIByIKCJoYCCBwJ6CLAYOHgIFBogKCLgYCCgEAAoNIA4WUBw56CkRrFAQWAB4J6FqQOEyAOBPQRrCPQYCCBwJ6CNYYOFoAPBAoQOIpAOBPQRcCRIwOBPQRcCTBB6CFIoOGeoYCIBwJ6DARCJCNYQOIRIRrDARAOCLgckydtAQaJDLgttcwICCRIRNFpu0AQYOEJpQODVQYOLTZKYCHw4OKHw4NFAAS8ELIgABA=="))
|
|
@ -0,0 +1,51 @@
|
|||
let course = require("Storage").readJSON("course_data_hole1.json");
|
||||
|
||||
console.log(Object.keys(course));
|
||||
|
||||
g.clear();
|
||||
|
||||
for (var feature of course.features) {
|
||||
//console.log(Object.keys(feature));
|
||||
if (feature.type === "fairway"){
|
||||
g.setColor(0,0,1);
|
||||
} else if ( feature.type === "tee"){
|
||||
g.setColor(1,0,0);
|
||||
} else if (feature.type === "green"){
|
||||
g.setColor(0,1,0);
|
||||
} else if (feature.type === "bunker"){
|
||||
g.setColor(1,1,0);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
var nodelist = [];
|
||||
feature.nodesXY.forEach(function (node) {
|
||||
nodelist.push(node.x);
|
||||
nodelist.push(node.y);
|
||||
});
|
||||
newnodelist = g.transformVertices(nodelist,{
|
||||
x: 150, // x offset (default 0)
|
||||
y: 150, // y offset (default 0)
|
||||
scale: 0.45, // scale factor (default 1)
|
||||
rotate: course.angle - 1.57, // angle in radians (default 0)
|
||||
});
|
||||
|
||||
g.fillPoly(newnodelist, true);
|
||||
console.log(feature.type);
|
||||
console.log(nodelist);
|
||||
}
|
||||
|
||||
var nodelist = [];
|
||||
course.nodesXY.forEach(function (node) {
|
||||
nodelist.push(node.x);
|
||||
nodelist.push(node.y);
|
||||
});
|
||||
|
||||
newnodelist = g.transformVertices(nodelist,{
|
||||
x: 150, // x offset (default 0)
|
||||
y: 150, // y offset (default 0)
|
||||
scale: 0.45, // scale factor (default 1)
|
||||
rotate: course.angle - 1.57, // angle in radians (default 0)
|
||||
});
|
||||
g.setColor(0,1,1);
|
||||
g.drawPoly(newnodelist);
|
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
|
@ -0,0 +1,114 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
|
||||
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<button id="upload" class="btn btn-primary">Upload</button>
|
||||
|
||||
<script src="../../core/lib/customize.js"></script>
|
||||
<script src="./maptools.js"></script>
|
||||
|
||||
<script>
|
||||
const url = "https://overpass-api.de/api/interpreter";
|
||||
let query = `[out:json][timeout:5];relation["name"="Davis Golf Course"];way(r)["golf"="hole"];foreach->.a(.a out;way(around.a:40.0)["golf"];out;>>;out;)`; // this gets everything at least
|
||||
let course_input = null;
|
||||
|
||||
function findNodeCoordinates(elements, id) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
if (elements[i].type === "node" && elements[i].id === id) {
|
||||
let thing = (({ lat, lon }) => ({ lat, lon }))(elements[i]);
|
||||
return thing;
|
||||
}
|
||||
}
|
||||
console.error("node id: ", id, " not found");
|
||||
}
|
||||
|
||||
function processFeatures(course_verbose) {
|
||||
let course_processed = {
|
||||
holes: {}
|
||||
};
|
||||
let current_hole = 0;
|
||||
for (let i = 0; i < course_verbose.length; i++) {
|
||||
const element = course_verbose[i];
|
||||
|
||||
if (element.type === "way") {
|
||||
// if we find a high-level hole feature
|
||||
if (element.tags.golf === "hole") {
|
||||
if (element.tags.ref in course_processed.holes) continue; // the around picks up hole ways that mess everything up
|
||||
current_hole = parseInt(element.tags.ref); //subsequent way features should be applied to the current hole
|
||||
let nodes = [];
|
||||
for (const node_id of element.nodes) {
|
||||
nodes.push(findNodeCoordinates(course_verbose, node_id));
|
||||
}
|
||||
var hole = {
|
||||
hole_number: current_hole,
|
||||
handicap: parseInt(element.tags.handicap),
|
||||
par: parseInt(element.tags.par),
|
||||
nodes: nodes,
|
||||
features: [],
|
||||
}
|
||||
|
||||
course_processed.holes[current_hole.toString()] = hole;
|
||||
}
|
||||
|
||||
// if we find a feature add it to the corresponding hole
|
||||
if (/(green)|(bunker)|(tee)|(teebox)|(fairway)|(water_hazard)/.test(element.tags.golf)) { //TODO missing cartpath
|
||||
let nodes = []
|
||||
for (const node_id of element.nodes) {
|
||||
nodes.push(findNodeCoordinates(course_verbose, node_id));
|
||||
}
|
||||
let new_feature = {
|
||||
nodes: nodes,
|
||||
type: element.tags.golf,
|
||||
id: element.id,
|
||||
}
|
||||
course_processed.holes[current_hole.toString()].features.push(new_feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return course_processed;
|
||||
}
|
||||
|
||||
function preprocessCoords(course_input) {
|
||||
// first do the high-level way
|
||||
for (var hole in course_input.holes) {
|
||||
course_input.holes[hole].nodesXY = arraytoXY(course_input.holes[hole].nodes, course_input.holes[hole].nodes[0])
|
||||
// then do the shapes in the features
|
||||
for (var feature in course_input.holes[hole].features) {
|
||||
course_input.holes[hole].features[feature].nodesXY = arraytoXY(course_input.holes[hole].features[feature].nodes, course_input.holes[hole].nodes[0]);
|
||||
}
|
||||
|
||||
// find out how the hole is angled
|
||||
course_input.holes[hole].angle = angle(course_input.holes[hole].nodesXY[0], course_input.holes[hole].nodesXY[course_input.holes[hole].nodesXY.length - 1])
|
||||
}
|
||||
return course_input;
|
||||
}
|
||||
|
||||
var out = {};
|
||||
// download info from the course
|
||||
$.post(url, query, function (result) {
|
||||
course_input = result;
|
||||
out = processFeatures(course_input.elements);
|
||||
out = preprocessCoords(out);
|
||||
console.log(course_input);
|
||||
console.log(out);
|
||||
})
|
||||
|
||||
// When the 'upload' button is clicked...
|
||||
document.getElementById("upload").addEventListener("click", function () {
|
||||
downloadObjectAsJSON(out.holes["1"], "course_data");
|
||||
sendCustomizedApp({
|
||||
storage: courses
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,59 @@
|
|||
const EARTHRADIUS = 6371000; //km
|
||||
|
||||
function radians(a) {
|
||||
return a * Math.PI / 180;
|
||||
}
|
||||
|
||||
function degrees(a) {
|
||||
let d = a * 180 / Math.PI;
|
||||
return (d + 360) % 360;
|
||||
}
|
||||
|
||||
function toXY(a, origin) {
|
||||
let pt = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
pt.x = EARTHRADIUS * radians(a.lon - origin.lon) * Math.cos(radians((a.lat + origin.lat) / 2));
|
||||
pt.y = EARTHRADIUS * radians(origin.lat - a.lat);
|
||||
return pt;
|
||||
}
|
||||
|
||||
function arraytoXY(array, origin) {
|
||||
let out = [];
|
||||
for (var j in array) {
|
||||
let newpt = toXY(array[j], origin);
|
||||
out.push(newpt);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function angle(a, b) {
|
||||
let x = b.x - a.x;
|
||||
let y = b.y - a.y;
|
||||
return Math.atan2(-y, x);
|
||||
}
|
||||
|
||||
function rotateVec(a, theta) {
|
||||
let pt = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
c = Math.cos(theta);
|
||||
s = Math.sin(theta);
|
||||
pt.x = c * a.x - s * a.y;
|
||||
pt.y = s * a.x + c * a.y;
|
||||
return pt;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/19721439/download-json-object-as-a-file-from-browser
|
||||
function downloadObjectAsJSON(exportObj, exportName) {
|
||||
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj));
|
||||
var downloadAnchorNode = document.createElement('a');
|
||||
downloadAnchorNode.setAttribute("href", dataStr);
|
||||
downloadAnchorNode.setAttribute("download", exportName + ".json");
|
||||
document.body.appendChild(downloadAnchorNode); // required for firefox
|
||||
downloadAnchorNode.click();
|
||||
downloadAnchorNode.remove();
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{ "id": "glfview",
|
||||
"name": "Golf View",
|
||||
"version":"0.01",
|
||||
"description": "This app will provide you with on course data to support your golf game! All information comes from OpenStreetMap. Please contribute by drawing your course using these guidelines: [guidelines](guidelines.com)",
|
||||
"icon": "golfview.png",
|
||||
"tags": "",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"custom": "index.html",
|
||||
"storage": [
|
||||
{"name":"glfview.app.js","url":"golfview.js"},
|
||||
{"name":"glfview.img","url":"golfview-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue