How do I draw a simple heading arrow in the centre of the screen?

I’m been using tskf’s offline gps map for a while and I think it’s a brilliant data field but I would like to see if I could add a heading arrow (the code is open source) as I feel it would be perfect for me then.

The datafield does show your position as a red dot in the middle of the screen and appears to be generated simply by just these bits of code in the function onUpdate(dc) part of the lone MC file.

dc.setColor(0xFF0000, -1);

dc.fillRectangle(x-2, y-2, 4,4);

Not being a coder (although I have tried scratch) I have no idea if that is the case but I was wondering if it would be possible to replace that with a small heading arrow instead in the same position and if so how? I’d be very grateful for some help.

P.S: Just in case you think I might be being lazy asking like this I have tried to do it myself for a few days by looking at the samples and documentation then typing stuff out on eclipse but I can’t find myself understanding it very well. 

P.P.S: I would ask tskf themselves but I don’t want to bother them seeing as the field doesn’t appear to have been updated for over a year now.

  • You need modify the code. Why don't you show the source code link

  • IMHO, you need do these stuff:

    1. find the direction (it needs e-compass sensor, I dont know if ur device supports)

    2. draw an arrow bitmap for the corresponding direction

    Not very difficult but I dont know if it is hard for u

  • Use fillPolygon and a line instead of a bitmap so it can be at the angle you want without needing a bunch of bitmaps.

    It's actually the same basic logic used to draw hands in the analog sample in the SDK,

  • Sorry for the late reply but I took another look at the analog sample today and somehow (by sheer luck I imagine) I copied in the correct parts from the sample and put them in the MC file, after a bit of fiddling about a pointer (shaped like the minute hand which is what I copied) showed up on the simulated vivoactive 3 along with the map. I replaced clock.Time or whatever it's called with activity.getactivityInfo.currentHeading. Inserting a fit file from ride with gps made it move to and fro which is good however until I'm able to test on my watch I have no idea if it's actually accurate.

    Here is the code:

    using Toybox.Application;

    using Toybox.Activity;

    using Toybox.WatchUi;

     

    class Map extends Application.AppBase

    { function getInitialView() {return [new DataField()];} }

     

    class DataField extends WatchUi.DataField

    { var screenCenterPoint;

      var curClip;

      var offscreenBuffer;

    function initialize() { DataField.initialize(); }

     

       function onLayout(dc) {

            x = dc.getWidth()/2;

            y = dc.getHeight()/2;

            screenCenterPoint = [dc.getWidth()/2, dc.getHeight()/2];}

           

     

        const ut=80.60, un=-28,

              dt=33.36, dn=45.08,

              imgs=250, rx=2000/imgs, ry=2000/imgs,

              tt=ut-dt, nn=dn-un;

     

        var x,y, ar=new[9], pos, mx,my;

     

     

        function find(t,n)

        {

            var ayy = ((ut-t)*ry/tt),

                axx = (rx-(dn-n)*rx/nn),

                 yy = ayy.toNumber(),

                 xx = axx.toNumber();

     

            my = (ayy-yy)*imgs;

            mx = (axx-xx)*imgs;

     

            var npos = yy*rx+xx;

            if (pos != npos)

            {

                var r = [ :m0, :m1, :m2, :m3, :m4, :m5, :m6, :m7,

                          :m8, :m9,:m10,:m11,:m12,:m13,:m14,:m15,

                         :m16,:m17,:m18,:m19,:m20,:m21,:m22,:m23,

                         :m24,:m25,:m26,:m27,:m28,:m29,:m30,:m31,

                         :m32,:m33,:m34,:m35,:m36,:m37,:m38,:m39,

                         :m40,:m41,:m42,:m43,:m44,:m45,:m46,:m47,

                         :m48,:m49,:m50,:m51,:m52,:m53,:m54,:m55,

                         :m56,:m57,:m58,:m59,:m60,:m61,:m62,:m63];

     

                pos = npos;

                for(var i=0, iy=yy-1; iy<=yy+1; iy++) {

                    for(var ix=xx-1; ix<=xx+1; i++, ix++) {

                        if(iy>=0 && iy<ry && ix>=0 && ix<rx) {

                            ar[i] = Rez.Drawables[r[iy*rx+ix]];}

                        else {ar[i]=null;} }}

            }

        }

        function generateHandCoordinates(centerPoint, angle, handLength, tailLength, width) {

            // Map out the coordinates of the watch hand

            var coords = [[-(width / 1), tailLength], [-(width / 4), -handLength], [width / 5, -handLength], [width / 1, tailLength]];

            var result = new [4];

            var cos = Math.cos(angle);

            var sin = Math.sin(angle);

     

            // Transform the coordinates

            for (var i = 0; i < 4; i += 1) {

                var x = (coords[i][0] * cos) - (coords[i][1] * sin) + 0.5;

                var y = (coords[i][0] * sin) + (coords[i][1] * cos) + 0.5;

     

                result[i] = [centerPoint[0] + x, centerPoint[1] + y];

            }

            return result;

     

     

        } function onUpdate(dc)

        { var g = Activity.getActivityInfo().currentLocation;

            var width;

            var height;

            var screenWidth = dc.getWidth();

            var heading = Activity.getActivityInfo().currentHeading;

            var minuteHandAngle;

            var targetDc = null;

           

            if(null != offscreenBuffer) {

                dc.clearClip();

                curClip = null;

                // If we have an offscreen buffer that we are using to draw the background,

                // set the draw context of that buffer as our target.

                targetDc = offscreenBuffer.getDc();

            } else {

                targetDc = dc;

           

            

     

            if (g!=null)

            {

                g = g.toDegrees();

                find(g[0], g[1]);

                g = null;

     

                for(var i=0, yy=y-imgs-my; yy<y+imgs; yy+=imgs) {

                    for(var xx=x-imgs-mx; xx<x+imgs; xx+=imgs, i++) {

                        if(ar[i]!=null){

                            dc.drawBitmap(xx, yy, WatchUi.loadResource(ar[i]));}}}

                           

                            }

            //Use white to draw the hour and minute hands

            width = targetDc.getWidth();

            height = targetDc.getHeight();

            targetDc.setColor(Graphics.COLOR_RED, Graphics.COLOR_TRANSPARENT);

            minuteHandAngle = (heading / 1.0) * Math.PI * 2;

            targetDc.fillPolygon(generateHandCoordinates(screenCenterPoint, minuteHandAngle, 70, 0, 2));

          

            }

           

        }

       

     }

     

  • 1. find the direction (it needs e-compass sensor, I dont know if ur device supports)

    Not necessarily. Devices which don't have a compass but do support navigation will use the GPS to determine the direction of travel, while you are moving. A compass is only required to show your heading when you are standing still or moving very slowly.

    After some quick googling, it looks like ActivityInfo.currentHeading will use either magnetic compass or GPS direction, at least for 735XT:

    https://forums.garmin.com/developer/connect-iq/f/discussion/3428/info-currentheading-not-returning-same-info-as-heading-data-field

    The documentation for ActivityInfo.currentHeading is not clear on this:

    The true north referenced heading in radians.

    This provides compass orientation if it is supported by the device.

    Note that there's also ActivityInfo.track, but it's not supported by CIQ 1:

    The current track in radians.

    Track is the direction of travel in radians based on GPS movement. If supported by the device, this provides compass orientation when stopped.

    My understanding is that heading is supposed to be direction you are pointed in, whereas track is supposed to be direction you are moving.

    This page explains the difference between Garmin's GPS compass, electronic compass, and 3-axis electronic compass (although it doesn't say what Vivoactive and Forerunner watches have).

    https://support.garmin.com/en-CA/?faq=X0caIZgf5j1eBqY2WMTuz5

    "Garmin Outdoor handhelds and watches will utilize one of three different compass options, seen below:

    • GPS Compass - Found in handheld devices lacking an electronic compass, this compass uses GPS to determine heading. This requires you to move at approximately 2-3 miles per hour in order to get a reading. When stopped, or moving at speeds slower than 2 miles per hour, the compass may not function as intended.
    • Standard Electronic Compass - The electronic compass functions more like a traditional compass. It can be used when idle, or moving at low speeds. The device must be held level in order to give a reading, and may at times require calibration.
    • 3-Axis Electronic Compass - Similar to the electronic compass, the 3-axis electronic compass can be used when idle, or moving at low speeds. This compass is also tilt compensated, meaning the device can be held in any position and will still give a compass reading. At times, this compass may also require calibration."
  • Please ignore that Garmin explanation, at best, it's confusing.

    There's no such thing as a "GPS Compass". GPS can tell you the direction your device is travelling, irrespective of the orientation of the device.

    A compass will tell you the direction to magnetic north relative to your device's orientation, irrespective of your direction of travel.

    There's no such thing as a "Standard Electronic Compass". A standard compass is magnetic, not electronic. 

  • Please ignore that Garmin explanation, at best, it's confusing.

    Fair enough. I don't think Garmin's terminology is great either, but my point is that your device can use GPS to tell what direction you are travelling in, even without a built-in compass.

  • It's actually a bit interesting as based on how you look at heading, it will be either the device compass or GPS based on your speed.  If you are standing still, it's the compass.  Above (if I recall) 2mph, it's GPS.

    • Okay, so from my testings I can see that my pointer appears to face the way i’m facing occasionally with gps on, if i’m facing n, s, e or w it sometimes points in the complete opposite direction or darts around a bit randomly. I think somehow i’ve accIdently made a compass that’s trying to point north but gps is messing with it or something because when I turn off gps it just points north no matter what way I face instead of what I want it to do which is point to my heading aka whers i’m facing. Can you see in my code I shared where I went wrong somewhere? Much appreciated for any help.
  • t's actually a bit interesting as based on how you look at heading, it will be either the device compass or GPS based on your speed.  If you are standing still, it's the compass.  Above (if I recall) 2mph, it's GPS.

    Yeah, this is how native heading works, too, IIRC.