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