howardjones / network-weathermap

Network Weathermap draws diagrams from data
http://www.network-weathermap.com/
MIT License
425 stars 95 forks source link

Programmatically inject a node at render time, possible feature. #207

Closed StephenCorcoran closed 5 years ago

StephenCorcoran commented 5 years ago

Hi Folks,

I'm a long term admirer of the project. Keep up the good work!!.

This might be one for Howie,

Say I want to programmatically inject a node adjacent to an existing map node during render time. For example a 'helper' node that would appear/disappear in sympthy with some external condition. I want to preserve the functionality of the 'parent' node. I'm temped to try this via the WeatherMapPreProcessor plugin.

With timerity I tried this approach which for testing is based on a node hint variable;

#############################################################################
#############################################################################
# Server and site specific from here on, sc.
#
# Jan, 2018 Attempt to add a helper node based on some external condition
#           using a map pre processor plugin.
#           Test a dummy node hint for the moment.
#############################################################################

         // Make sure the user has done his bit
        if(!isset($map->hints["map_context"])) {
            if($map->context=='cli') {
                print("WARNING: cannot find 'map_context' directive in map configuration file. Quitting map setup routine.\n");
            }
            return(FALSE);
        }

        # Vector report
        $doMerge;
        $lastId=    end($map->nodes)->id; // last nodeId after conf parse?
        foreach($map->nodes as $index => $node) {

            if (isset($node->hints["vectorReport"])) {

                # Picked up a test hint, populate the new node object
                $defnode=                       new WeatherMapNode;
                $newInstanceName=               $node->name . "_report";

                # the hash keys . . .
                $defnode->id=                   ++$lastId;
                $defnode->name=                 $newInstanceName;

                # Use the cardinal offset function?
                # $defnode->labeloffset=        'S';    # this bird may have flown . . .
                $defnode->x=                  $node->x + 100;
                $defnode->y=                  $node->y + 100;

                # Minimal config to get us on the map?
                $defnode->iconfile=           "/home/mapman/weathermap/images/editor/report_18_18.png";
                $defnode->label=              'report';
                $defnode->infourl[0]=         "/~mapman/fastcgi/development/vectorReportTest.fcgi?vector=" . $map->hints["map_context"] . "." . $node->name;
                $defnode->notestext[0]=       "Follow link for vector report";
                $defnode->overlibwidth=       350;
                $defnode->overlibheight=      200;
                $defnode->overliburl[0][0]=    "function";
                $defnode->overliburl[0][1]=    "fnVectorPopup('vector=" . $map->hints["map_context"] . "." . $node->name . "&delta=8days')";

                # just 'inherit' the parent to keep the noise down when rendering
                $defnode->labelfont=            $node->labelfont;
                $defnode->inpercent=            $node->inpercent;
                $defnode->outpercent=           $node->outpercent;
                $defnode->overlibcaption=       $node->overlibcaption;
                $defnode->max_bandwidth_in=     $node->max_bandwidth_in;
                $defnode->max_bandwidth_out=    $node->max_bandwidth_out;
                $defnode->max_bandwidth_in_cfg= $node->max_bandwidth_in_cfg;
                $defnode->max_bandwidth_out_cfg=$node->max_bandwidth_out_cfg;

                # Add new to temp hash 
                $addNode[$newInstanceName]= $defnode;
                $doMerge=                   1;
               }
        }

        # See if we have anything
        if (isset($doMerge)) {
            $map->nodes=    array_merge($map->nodes,$addNode);
            # echo > stdout for a look
            foreach($map->nodes as $index => $node) {
                print $index . "=>" . $node->id . "=>". $node->name . "\n";
            }
        }

Hmmmm . . ., map->nodes is updated and the rendering runtime 'seems' to pick up the additions. For example I get a couple of 'ColourFromPercent: Attempted to use non-existent scale' warnings for the added node. But nothing appears on the canvas!. In the resultant html the 'id' counter above is clobbered by the links collection.

Anything obvious in the code, or is it worth the effort?

Running Weathermap98/PHP 5.4.16/CentOs7 . . .

Thanks and Regards, Stephen

howardjones commented 5 years ago

Can I have a bit of context? Where are you putting this code?

howardjones commented 5 years ago

It's OK, I see where you are. A few comments:

1) If you look at ReadConfig, you can see how that creates nodes. You don't need to manage all the inheritance, or IDs.

     $curnode=new WeatherMapNode;
     $curnode->name=$matches[2];
     $curnode->Reset($this);

(where $this is $map) That copies everything from the DEFAULT node. If you want it to be from a different one (like the 'parent' node), you can add $curnode->template="othernodename" before the Reset, and it will use that as a template instead. Reset also sets the id property. So you only need to add the properties that are actually different (icon, overlib), after the Reset has copied the template.

2) You would already be too late in the process to deal with relative positioning unfortunately.

3) It definitely feels like there should be an easier way to do this :-)

4) This will almost certainly break when 1.0 is done. The internals have changed a fair bit. At least the property names and class names for various things.

StephenCorcoran commented 5 years ago

Thanks for the response.

If there is the demand you might consider such a method in a future release, let's see!.

Point four above is important. Until then your guidance produces a result;

        #############################################################################
        #############################################################################
        # Jan, 2018 Attempt to add a helper node (based on an external condition)
        #           using a map pre processor plugin. For the moment we test a
        #           node dummy hint, sc.
        # Feb, 2018 Tidy up as suggested, https://github.com/howardjones (regards)
        #############################################################################

        # Iterate the $map->nodes collection and set $addNode with candidate nodes
        $addNode=   array();
        foreach($map->nodes as $key => $node) {
            if (isset($node->hints["vectorReport"])) {
                array_push($addNode,$key);      # Remember the 'parent' node Larry Wall style
            }
        }

        # addNode will be set with nodeNames that contained the hint. Iterate and add an
        # additional 'helper' node to the $map->nodes collection. Adjust properties via hints
        # or external samaohores.
        foreach($addNode as $value) {

            $newName=                           $value . "_report";     # New node name and
            $defnode=                           new WeatherMapNode;     # new class instance.
            $defnode->name=                     $newName;               # Howie style, set instance name
            $defnode->Reset($map);                                      # and instantiate with defaults.
            $map->nodes[$newName]=              $defnode;               # Save to nodes collection

            # Populate properties
            $map->nodes[$newName]->x=           $map->nodes[$value]->x + 25;
            $map->nodes[$newName]->y=           $map->nodes[$value]->y + 25;
            $map->nodes[$newName]->iconfile=    "/home/mapman/weathermap/images/editor/report_18_18.png";
            $map->nodes[$newName]->label=       "";
            # Etc, etc . . .

            # Gotcha, push the node reference into the zlayers collection or it will be ignored
            array_push($map->seen_zlayers[$defnode->zorder],$map->nodes[$newName]);
        }
        # And return. The rendering environment will see an
        # augmented $map->nodes collection . . .
        #############################################################################
        #############################################################################

Got caught with the xlayers collection which is set in ReadConfig method. Had to dig around a bit.

Best regards, Stephen