I Need Help with making a function that draws in a canvas using location data.


Joined
Dec 14, 2021
Messages
12
Reaction score
0
Well, I have this code it is in a file called map.js and it helps me draw on the canvas, so here are the files:


index.html:
HTML:
<!DOCTYPE html>
<html>
    <head>
        <meta name="build-version" content="V00.001.00" />
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta name="apple-mobile-web-app-capable" content="no"/>
        <title>Mapper Tool</title>
        <link href="style.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <h1>Mapper Tool</h1>
        <p id="tteesstt"></p>
        <p style="text-align:center;font-size:16px;">
            <label style="font-size:20px;" for="zoom">Zoom (Currently: <span id="currentZoom">100%</span>):</label>
            <br><br>
            25%<input type="range" name="zoom" id="zoom" min="25" max="500" defaultValue="100" value="100" step="5" onchange="zoom('change')">500%
            <br><br>
            <label for="zoom"><button id="zoomReset" class="tab style-tab2" onclick="zoom('reset')">Zoom Reset</button></label>
        </p>
        <p style="text-align:center;font-size:16px;">
            <label for="lineSize">Size of the Lines: </label>
            <input type="number" name="lineSize" id="lineSize" min="2" max="20" defaultValue="2" value="2" onchange="draw('lineSize','','yes')">
            <!--<button onclick="draw('lineSize','','yes')">Submit</button>-->
        </p>
        <div id="mapContainer">
            <div id="mapCur">
                <canvas id="mapDrawer" width="6000" height="6000">Your browser does not support the canvas element.</canvas>
                <!--<iframe src="map/map.html" title="The Map" width="6000" height="6000"></iframe>-->
            </div>
        </div>
        <div id="mapSpacer"></div>
        <div id="footSpacer"></div>
        <footer>
            <div id="footContent">
                <p>Thank you for using AAE's Mapper Tool.</p>
            </div>
        </footer>
        <!-- non-resource JS files -->
        <script src="map/map.js"></script>
        <script src="script.js"></script>
    </body>
</html>


map.js:
JavaScript:
var linSiz = 0;

function draw(type,content,drawIt) { // Put no spaces in content unless it is in ''
    var canvas = document.getElementById("mapDrawer");
    var ctx = canvas.getContext("2d");
    
    var lineSize = parseFloat(document.getElementById("lineSize").value);
    ctx.lineWidth = lineSize;
    ctx.lineJoin = "round";
    ctx.lineCap = "round";
    var dataCon = content.split(",");
    function drawLine(x1,y1,x2,y2) {
        ctx.moveTo(x1,y1); // start location of line
        ctx.lineTo(x2,y2); // end location of line
    }
    function drawArc(x,y,r,sAngle,eAngle,conterclockwise){ctx.arc(x,y,r,sAngle,eAngle,conterclockwise);}
    function drawArcTo(x1,y1,x2,y2,x3,y3,x4,y4,x5,y5,r) {
        ctx.moveTo(x1,y1);  // Create a starting point
        ctx.lineTo(x2,y2);  // Create a horizontal line
        ctx.arcTo(x3,y3,x4,y4,r);  // Create an arc
        ctx.lineTo(x5,y5); // Continue with vertical line
    }
    function error(text,x,y) {
        ctx.font = "normal normal 900 300px Arial";
        ctx.fillText(text,x,y);
    }
    if (type == "lineSize") {
        if (linSiz == 0) {
            var cmfmLnSz = confirm("Are you sure you want to change the line size?\n\nThis action cannot be undone (I think). If you want to proceed click OK.");
            if (cmfmLnSz == true) {
                var cmfmLnSzMssRemove = confirm("You have decided to continue.\n\nDo you want to never see this again?\nThen click OK.");
                if (cmfmLnSzMssRemove == true) {
                    alert("You have decided to continue, we will stop showing this message.");
                    ctx.lineWidth = lineSize;
                    linSiz = 1;
                } else {
                    alert("You have decided to cancel, we will keep showing this message.");
                    ctx.lineWidth = lineSize;
                }
            } else {
                document.getElementById("lineSize").value = 2;
                lineSize = document.getElementById("lineSize").value;
                ctx.lineWidth = lineSize;
                alert("You have decided to cancel, we will not change the line size.");
            }
        } else {
            ctx.lineWidth = lineSize;
        }
    } else if (type == "line") {
        drawLine(parseFloat(dataCon[0]),parseFloat(dataCon[1]),parseFloat(dataCon[2]),parseFloat(dataCon[3]));
    } else if (type == "arc") {
        drawArc(parseFloat(dataCon[0]),parseFloat(dataCon[1]),parseFloat(dataCon[2]),parseFloat(dataCon[3]),parseFloat(dataCon[4]),dataCon[5]);
    } else if (type == "arcTo") {
        drawArcTo(parseFloat(dataCon[0]),parseFloat(dataCon[1]),parseFloat(dataCon[2]),parseFloat(dataCon[3]),parseFloat(dataCon[4]),parseFloat(dataCon[5]),parseFloat(dataCon[6]),parseFloat(dataCon[7]),parseFloat(dataCon[8]),parseFloat(dataCon[9]),parseFloat(dataCon[10]));
    } else if (type == "error") {
        error("Error: "+dataCon[0],parseFloat(dataCon[1]),parseFloat(dataCon[2]));
    }
    if (drawIt == "yes") {
        ctx.stroke();
    } else {}
}

// testing
draw("line","3000,0,3000,10");
draw("line","3000,10,3200,10");
draw("line","3200,10,3400,100");
draw("line","3400,100,3400,200");
draw("line","3400,200,3000,200");
draw("line","3000,200,2800,100");
draw("line","2800,100,2400,100");
draw("line","2400,100,2400,600");
draw("line","2400,600,1400,800");
draw("line","1400,800,1200,700");
draw("line","1200,700,1200,600");
draw("line","1200,600,1000,400");
draw("line","1000,400,600,400");
draw("line","600,400,600,200");
draw("line","600,200,4000,300");
draw("line","4000,300,4200,100");
draw("line","4200,100,4400,200");
draw("line","4400,200,3000,100");
draw("line","3000,100,3000,10","yes");
// end testing

function mapping(type) {
    if (type == "handDrawn") {
        // some code
    } else if (type == "gpsMade") {
        function getLocation() {
            if (navigator.geolocation) {
                navigator.geolocation.watchPosition(showPosition, showError);
            } else {
                draw("error","Geolocation is not supported by this browser.,10,400");
            }
        }
        function showPosition(position) {
            //x.innerHTML="Latitude: " + position.coords.latitude + "<br>Longitude: " + position.coords.longitude;
            if (position.coords.heading != null) {
                
            } else {
                draw("error","Could not retrive heading.,10,400");
            }
        }
        function showError(error) {
            switch(error.code) {
                case error.PERMISSION_DENIED:
                    draw("error","User denied the request for Geolocation.,10,400")
                    break;
                case error.POSITION_UNAVAILABLE:
                    draw("error","Location information is unavailable.,10,400")
                    break;
                case error.TIMEOUT:
                    draw("error","The request to get user location timed out.,10,400")
                    break;
                case error.UNKNOWN_ERROR:
                    draw("error","An unknown error occurred.,10,400")
                    break;
            }
        }
    }
}


script.js:
JavaScript:
function getRndInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1) ) + min;
}

function setCookie(cname, cvalue) {
    document.cookie = cname + "=" + cvalue + ";";
}

function getCookie(cname) {
    let name = cname + "=";
    let ca = document.cookie.split(';');
    for(let i = 0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

/* -------------------------------------------------------------------------------- */

function zoom(type) {
    var container = document.getElementById('mapContainer');
    var zoomSlider = document.getElementById("zoom");
    var currentZoom = document.getElementById("currentZoom");
    var mapCur = document.getElementById("mapCur");
    if (type == "change") {
        var topPercent = (container.scrollTop + container.clientHeight/2) / container.scrollHeight;
        var leftPercent = (container.scrollLeft + container.clientWidth/2) / container.scrollWidth;
        mapCur.style.transform = "scale(" + (zoomSlider.value/100) + "," + (zoomSlider.value/100) + ")";
        var newTop = container.scrollHeight * topPercent - (container.clientHeight/2);
        var newLeft = container.scrollWidth * leftPercent - (container.clientWidth/2);
        container.scrollTo(newLeft, newTop);
        currentZoom.innerHTML = zoomSlider.value + "%";
    } else if (type == "reset") {
        zoomSlider.value = 100;
        currentZoom.innerHTML = zoomSlider.value + "%";
        zoom("change");
    }
}
zoom("change");


style.css:
CSS:
/* elements */

body {
    overflow-x: hidden;
}

button {
    font-size: 1.171303074670571vw; /* 16px */
    cursor: pointer;
}

p {
    font-size: 1.171303074670571vw; /* 16px */
}

span {
    font-size: 1.171303074670571vw; /* 16px */
}

a {
    font-size: 1.171303074670571vw; /* 16px */
    text-decoration: none;
}

img {
    width: 100%;
    pointer-events: none;
}

iframe:focus {
  outline: none;
}

iframe[seamless] {
  display: block;
}

/* Headers */

h1, h2, h3, h4, h5, h6 {
    text-align: center;
}

h1 {
    font-size: 7.320644216691069vw; /* 80  100px */
}

h2 {
    font-size: 5.856515373352855vw; /* 40   80px */
}

h3 {
    font-size: 4.392386530014641vw; /* 30   60px */
}

h4 {
    font-size: 2.9282576866764276vw; /* 20   40px */
}

h5 {
    font-size: 1.4641288433382138vw; /* 18    20px */
}

h6 {
    font-size: 1.171303074670571vw; /* 16px */
}

/* footer -/||\- header */

header {
    display: block;
    text-align: center;
    /*padding: 0.21961932650073207vw; 3px*/
}

footer {
    display: block;
    padding: 1.4641288433382138vw; /* 20px */
    background: black;
    color: white;
    overflow-x: hidden;
    position: absolute;
    left: 0;
    width: 100%;
    /*
    position: relative;
    bottom: 0;
    width: 100%;
    */
}

#footSpacer {
    height: 16.837481698389457vw; /* 230px */
}

#footBtns {
    display: none;
    text-align: center;
}

#footContent {
    text-align:center;
}

/* -------------------------------------------------------------------------------- */

.hrSeps {
    border: 1px solid black;
    width: 100%;
    position: absolute;
    left: 0;
}

.ss {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    -o-user-select: none;
    user-select: none;
}

.buttons {
    text-align: center;
}

.hiddenTab {
    visibility: hidden;
    position: absolute;
}

.tabs {
    text-align: center;
}

.tab {
    background-color: #008CBA; /* Blue */
    border: none;
    color: white;
    padding: 16px 32px; /* 1.171303074670571vw 2.342606149341142vw */
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 16px; /* 1.171303074670571vw */
    /*margin: 4px 2px;*/
    transition-duration: 0.4s;
    cursor: pointer;
}

.style-tab1 {
    background-color: white;
    color: black;
    border: 2px solid #008CBA;
}

.style-tab1:hover {
    background-color: #008CBA;
    color: white;
}

.styletab1-active, .style-tab1:active {
    background-color: #ff7398;
    color: black;
    border: 2px solid #ff7398; /* 0.14641288433382138vw */
}

.style-tab2 {
    background-color: white;
    color: black;
    border: 2px solid #ff0043; /* 0.14641288433382138vw */
}

.style-tab2:hover {
    background-color: #ff0043;
    color: white;
}

.styletab2-active, .style-tab2:active {
    background-color: #ff7398;
    color: black;
    border: 2px solid #ff7398; /* 0.14641288433382138vw */
}

.pbtnU {
    font-size: 1.4641288433382138vw; /* 20px */
}

.btnU {
    background-color: #ff0043;
    border: 0.29282576866764276vw solid #ff7398; /* 4px */
    color: white;
    padding: 0.8784773060029283vw 2.049780380673499vw; /* 16px 32px     12px 28px */
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 1.4641288433382138vw; /* 16px   20px */
    /*margin: 4px 2px;*/
    transition-duration: 0.4s;
    cursor: pointer;
}

.btnU:hover {
    background-color: #ff7398;
    border: 0.29282576866764276vw solid #ff0043; /* 4px */
    color: black;
}

.imgCenter {
    display: block;
    margin-left: auto;
    margin-right: auto;
}

.clearfix::after {
  content: "";
  clear: both;
  display: table;
}

.centerTxt {
    text-align: center;
}

.pre {
    font-family: monospace;
}

.pre p {
    font-weight: bold;
    white-space: pre;
    margin: 1.171303074670571vw 0; /* 1em // 16px */
}

/* -------------------------------------------------------------------------------- */

#currentZoom {
    font-size: 16px;
}

#zoom {
    -webkit-appearance: none;
    appearance: none;
    width: 73.20644216691069vw; /* 1000px */
    height: 12px; /* 0.8784773060029283vw */
    border-radius: 6px;
    background: #ff7398;
    outline: none;
    opacity: 0.7;
    -webkit-transition: .2s;
    transition: opacity .2s;
}

#zoom:hover {
    opacity: 1;
}

#zoom::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: #ff0043;
    cursor: pointer;
}

#zoom::-moz-range-thumb {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: #ff0043;
    cursor: pointer;
}

#zoomReset {
    font-size: 16px;
}

#lineSize {
    width: 40px;
}

#mapContainer {
    overflow: scroll;
    width: 99.38%;
    height: 99.38%;
    position: absolute;
    left: 0;
    border: 4px solid #c3c3c3;
}

#mapCur {
    transform-origin: 0 0;
}

#mapDrawer {
    width: 6000px;
    height: 6000px;
}

#mapSpacer {
    height: 500px;
}

Well, I am trying to make a function called mapping(type), its primary job would be creating the map, right now I am just working on the gpsMade type. All that I want help with doing is making the drawing part, which is the internal function showPosition(position). The drawing ways that I want is; the start of the gpsMade drawing would be the center of the canvas, the heading of North would be up, also a mile in the map will be the equivalence of 100px, so all I need help with doing is making it so that the direction of travel can be drawn in the correct direction and that it would continuously draw as long as you are in motion and stop drawing when you stop and start drawing again when you start moving.
 
Ad

Advertisements

Joined
Mar 3, 2021
Messages
174
Reaction score
19
I'm gonna take some shots in the dark, 'cause I'm not 100% understanding. But, have some numbers and assumptions. A real problem here is that you're not declaring a coordinate system, so it's gonna get a little... wonky. More to follow.

The easiest way would just be store the last location and, on each update, just draw a line between the new and old coordinates. If it updates once a second or every few seconds, that sounds like it should be frequent enough to be real-enough time.

If you're trying to go real-time and predictive between updates, you can turn heading into slope or delta using some simple trig. Grab position.coords.speed and use it to calculate new coordinates. I'll assume that you continuously store an X and a Y coordinate for the last position in the canvas that was painted and the time between paintings.

JavaScript:
var deltaX = cos(deg2rad(90-position.coords.heading));
var deltaY = sin(deg2rad(90-position.coords.heading));
var distanceMeters = position.coords.speed * secondsSinceLastUpdate;
var distancePixels = distanceMeters * 100 * 0.000621371;
var newX = deltaX * x;
var newY = deltaY * y;

This is for a flat Cartesian plane, like your canvas. Unfortunately, latitude, longitude, and GPS are meant to be projected on an ellipsoidal Earth. If you want to do this hardcore, use Vincenty's direct problem to take coordinates, azimuth (heading), and distance into a new set of coordinates and a new heading (which changes because, e.g, a 10x10 degree grid of lat/lon is not a square). Now you can draw a proper arc on an ellipsoid shaped just like Earth. But, your canvas ain't that. So, you take your coordinates and run them through a map projection, equirectangular (also known as plate carrée) being an easy, popular choice. And, now, your map matches up with real life and even GIS programs. If you are just painting between two coordinates gotten directly from updates, you can skip straight to the map projection. Since the distances are pretty minimal, figuring out smooth arcs between points instead of using straight lines is probably overkill.

Is that at all helpful or did I completely miss the mark?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top