Graph and image display

Hi Gents,

I'd say I'm something like 80% done with my project.

I'd need your input on these two last parts : 

  1. I would like to be able to display a water level graph depending on the tidal data I retreive, such as this one : http://maree.info/125

         is there a tool to do so in the Garmin API? If not, is there a known API I could use?

       2. I would like to diplay an image on the watch face. This image will change depending on a value I retrieve from a webrequest() call. Would you have some tips on where to start?

Cheers,

Nico

  • 1) having a smooth sine wavy is tricky with all the labels, but a simple graph is easy.  Consider this one:

    I know the size of the graph and determine the Y scale based on min/max.  Let's say the temp is 60 to 80, and the graph itself us 50 pixels high.so with simple math, you determine where the current "y" is  

    This one is a bit different where I do a bargraph for HR:
    Just loop through the data, knowing the height of the graph and the min/max to determine the scale for the line, and with that, draw the current line.

    2) As far as an image, just include them all in your resources, and load and display the one you want based on the data

  • Hi Jim, 

    Thanks a lot.

    I started tackling the water Level graph dev. I managed to retreive the 24 height values and process them so I turn a height in meters into a height in pixels. I will display one rectangle per hour, taking this "processed height" as a parameter.

    To avoid using too much memory (or at least to keep things from going wrong) I use the same array to bring the data in, and to store the processed data once done (data[i] = data[i] * formula ).

    The thing is I would like to be able to access this array from MyWatchFaceView so I can use a FOR loop to iterate on data and display the 24 rectangles.

    At the moment, while being in the onReceive() function, my background stops working (Prompting onStop) everytime I try, in MyWatchFaceApp, to copy these values out in an array that is declared out of the class MyWatchFaceApp.

    From what I see, to be able to see and use a variable in View.mc, it has to be declared out of the class MyWatchFaceApp in App.mc.

    Could you shed some light on this?

    Once I'll be able to see and use the data in View.mc, in onUpdate() I plan to go for  : 

    for (var j = 0; j < 23 ; j++){
    dc.drawRectangle( (X , Y , WIDTH , data(j) );
    }

    Does that sound relevant to you?

    Cheers,

    Nico

  • Couple of things.  Here's one way to access variables in the view from AppBase without using globals:  I create the view, set a couple things in it.

    function getInitialView() {
        var view=new bgwfView();
        if( Toybox.WatchUi.WatchFace has :onPartialUpdate ) {
            view.do1hz=true;
            view.canDo1hz=true;
        }
        return [ view ];
    }

    As far as handling onStop in the main app as far as the background:

    class bgwfApp extends App.AppBase {
    	var inBackground=false;
        
        function initialize() {
            AppBase.initialize();
        }
    
    
        // onStop() is called when your application or background is exiting
        function onStop(state) {
            if(inBackground) {
                //background ending
            } else {
                //main app ending
            }
        }
    
        function getServiceDelegate(){
        	inBackground=true;
            return [new BgBLE.BLEServiceDelegate()];
        }
    }

  • Hi Jim,

    Thanks for your feedback.


    Correct me if I'm wrong but in your first example, if view.do1hz was an array, and the values you want to give to it were processed into another parsing function that uses onBackgroundData(data) as an input, even if you could ask the parsing function to return an array and use this (view.do1hz = returneddata), it looks like the getInitialView() is called before onBackgroundData(data). So the parsing function can not be called yet right?

    On my end, the parsing function is called by onBackgroundData(data).

    I mean, my parsing function and getInitialView() don't have common variables and getInitialView() is called before parsingFuntion() so I don't really see how I could make this work :/

    Nico

  • Assuming that view is a variable in the AppBase class, in onBackgroundData you can reference any variable in your view.

    so 

    function onBackgroundData(data) {

      view.myArray=data;

    }

    for example

  • Oh, OK, I get it now! :)
    I'm going to try that.

    Thanks! :)

  • Hi Jim,

    Having a look at displaying images now.

    I have a variable giving the angular value I want to represent. Each image is declared in drawable.xml with this type of naming : ArrowXX where "XX" is the angular value.

    At first I was thinking I could recreate the bitmap id by just putting in a string : 

    var ArrowBMP = "Arrow" + WindDirectionDisplayRounded;

    However this doesn't work as the dc.drawBitmap() functioin sees it's a string an not "an object of type WatchUi.BitmapResource".

    Is there a workaround so I can avoid writting one IF per angular value?

    Cheers,

    Nico 

  • You want to use a symbol and not a string.  There are a number of threads here that talk about that. 

    What I do is just draw draw arrows and don't use bitmaps, which also allows changing colors .  Here's a compass in one of my apps for example  (arrow is north here ) 

    or a more traditional compass:

  • EDIT: this comment is just a general example for the use case of efficiently selecting one symbol out of many. It is not meant to apply to this specific situation.

    As Jim said, you can't load resources by string, you load them by symbol. (It's not possible to convert a string to a symbol.)

    If you want to avoid awkward and relatively costly if-statements (with regards to memory usage), you can use an array as a lookup table, assuming that WindDirectionDisplayRounded is distributed evenly (or in a way that can be expressed with a mathematical formula which translates the value to an array index). (Note that using a dictionary would probably not be a memory-efficient solution, as dictionaries are very expensive).

    // For this example, assume that WindDirectionDisplayRounded takes on the values 0, 45, 90, ..., 315
    var arrowSymbols = [
        :Arrow0,
        :Arrow45,
        :Arrow90,
        ...
        :Arrow315
    ];
    
    // if the above assumption doesn't hold, would need to do additional math/checks to ensure
    // that the index to arroySymbols stays in bounds
    var bitmapSymbol = arrowSymbols[(WindDirectionDisplayRounded / 45).toNumber()];

  • And if you just skip bitmaps and resources and draw your own arrow, you free up memory of the lookup and bitmap, and can draw at any angle.  The basic logic for that is in the analog sample in the SDK for when it's drawing hands.