Node-RED Geofence node

We’ve been looking at some new location based uses for Node-RED recently. To this end we’ve been standardising on adding the information under the msg.location property.

msg: {
  topic: "foo",
  payload: "bar",
  location: {
    lat: 51.02477
    lon: -1.39724
  }
}

As part of this I’ve been writing a geofence node. This will allow messages to be filtered against specific regions.

The first pass worked well supporting both circular and rectangular regions, but configuring it by entering coordinates is not the most user friendly.

Screenshot from 2014-11-11 22:23:33

After a bit of playing with leafletjs and the leaflet.draw plugin I managed to come up with this which should be a lot more intuitive.

Geofence config dialog

At the moment it needs to load the leaflet js and css from a external server until we come up with a way to bundle static content inside nodes. This shouldn’t really be a problem as it’s having to load the map tiles from the internet as well. I’d like to find a way to pass in a base URL for the map server and to gracefully fall back to the text version if there is no access to a map server.

The map version also supports creating arbitrary polygon regions by joining up multiple points.

I’m still trying to get the config dialog to gracefully fall back to the basic version when there is no access to a mapserver but having problems with detecting the failure to download a map title. Any pointers would be gratefully accepted.

The code is on github and on npm. It can be installed with:

npm install node-red-node-geofence

35 thoughts on “Node-RED Geofence node”

  1. I cannot get the node to show any signs of life. So I am not sure how to manage its input parameters. I have tried all sorts of payload combinations – the closest I think to your info above is as follows.

    { “topic”: “NaheTor”, “payload”: “bar, location: { lat: 50.898486 lon: -1.39724 }”, “qos”: 0, “retain”: false, “_msgid”: “33eb05e.fcc14fa” }

    The first parameter of the payload is only there to try an match the format you have above but as I am using MQTT transport I have a few extra things in the message. The quotes are inserted by MQTT. Any advice much appreciated.

    1. Neil,

      Not sure what you mean about showing signs of life, but your message object is going to need tweaking a bit. You will most likely need to use a function node to move somethings around after they arrived via MQTT.

      All the geo nodes work on a top level key in the message object of ‘location’ which has keys of ‘lat’ and ‘lon’. So assuming you can drop the ‘bar,’ from the incoming MQTT message you could use the following code in a function node to build what you need:


      var loc = JSON.parse('{' + msg.payload + '}');
      msg.location = loc.location;
      return msg;

      This wraps the ‘location: {lat:…, lon:…}’ in an extra set of {} to make it parse as JSON properly, parses it, then inserts the values at the right level in the msg object.

      Now the Geofence node should be able to filter based on these values.

  2. Hi, I have being trying to get your Geofence node to work. But my input is still not excepted. Can you please have a look at the simple Node-Red example I made and correct my mistakes. Thank you in advance.

    [{“id”:”ce181fc0.31e7e”,”type”:”geofence”,”z”:”3659906f.c9a67″,”name”:”Geofence”,”mode”:”circle”,”inside”:”both”,”rad”:42.7951854517347,”points”:[],”centre”:{“latitude”:51.98473270844999,”longitude”:5.105298757498531},”x”:440.6666259765625,”y”:89.66666412353516,”wires”:[[“829277b7.7d6d88”]]},{“id”:”829277b7.7d6d88″,”type”:”debug”,”z”:”3659906f.c9a67″,”name”:””,”active”:true,”console”:”true”,”complete”:”true”,”x”:581.6665954589844,”y”:90.66666412353516,”wires”:[]},{“id”:”25d7ac56.da2854″,”type”:”template”,”z”:”3659906f.c9a67″,”name”:””,”field”:”location”,”format”:”handlebars”,”template”:”{{Location.lat}}\n{{Location.lon}}\n”,”x”:303.6666564941406,”y”:90.66666412353516,”wires”:[[“ce181fc0.31e7e”]]},{“id”:”2dab52b9.d254ae”,”type”:”inject”,”z”:”3659906f.c9a67″,”name”:””,”topic”:””,”payload”:”{lat:51.99024,lon:5.08360}”,”payloadType”:”string”,”repeat”:””,”crontab”:””,”once”:false,”x”:172.66665649414062,”y”:89.66666412353516,”wires”:[[“25d7ac56.da2854”]]}]

    1. A couple of problems.

      1. The template node will not convert a string to a JSON object, you need to user the JSON parse node
      2. The Geofence node works on the mag.location property not the msg.payload

      I have updated your example flow to now work:

      [{"id":"8668751e.646b78","type":"geofence","z":"8670e20f.e636c","name":"Geofence","mode":"circle","inside":"both","rad":42.7951854517347,"points":[],"centre":{"latitude":51.98473270844999,"longitude":5.105298757498531},"x":542,"y":369,"wires":[["8f24a472.213f7"]]},{"id":"8f24a472.213f7","type":"debug","z":"8670e20f.e636c","name":"","active":true,"console":"true","complete":"true","x":681,"y":367,"wires":[]},{"id":"df5bf5d9.958588","type":"inject","z":"8670e20f.e636c","name":"","topic":"","payload":"{\"lat\":51.99024, \"lon\":5.08360}","payloadType":"string","repeat":"","crontab":"","once":false,"x":110.00003051757812,"y":366,"wires":[["2b7c2f94.a1ef88"]]},{"id":"2b7c2f94.a1ef88","type":"json","z":"8670e20f.e636c","name":"","x":256,"y":369,"wires":[["62939d17.e04cf4"]]},{"id":"62939d17.e04cf4","type":"function","z":"8670e20f.e636c","name":"","func":"msg.location = msg.payload;\nmsg.payload = \"foo\";\nreturn msg;","outputs":1,"noerr":0,"x":398,"y":368,"wires":[["8668751e.646b78"]]}]

        1. Plying with the flow you provided above, but when I deploy it, I get the following error:

          Unexpected string in JSON at position 15

          1. Which one? There are several flows, but most are specific to the person asking a question.

            The one in Robert van der Veur’s post has all the quotes characters screwed up (it has both opening and closing quotes which is wrong) so won’t ever work, the one in my response to him works just fine.

  3. Hi,
    with MQTT Input node i’ll get following msg Object:

    { “topic”: “owntracks/phone/my”, “payload”: “{\”_type\”:\”location\”,\”lat\”:52.5367607,\”lon\”:8.1986322,\”tst\”:1449240737,\”acc\”:972,\”batt\”:78,\”tid\”:\”m7\”}”, “qos”: 1, “retain”: false, “_topic”: “owntracks/phone/my”, “_msgid”: “5ba1419f.a45ec” }

    Now i tried to convert it into msg.location:

    var loc = JSON.parse(msg.payload);
    msg.location = “{lat: ” + loc.lat + “, lon: ” + loc.lon + “}”;
    return msg;

    This leads to an error message:

    TypeError: Cannot call method ‘toString’ of undefined

    Any hint to to convert the string to an acceptable JSON (guess this is the failure)?

    1. got it:

      var loc = JSON.parse(msg.payload);
      var newmsg = { lat: loc.lat, lon: loc.lon };
      msg.location = newmsg;
      return msg;

      Great Node ๐Ÿ™‚

  4. Good effort dude. I am having a problem with OpenStreetMap as most of map listing in my area are on Google maps. How can I use my Google maps api key with your geofence-node? I think I have to modify javascript code inside geofence.html file. May you suggest any solution?

    1. You will have to read the doc about using LeafletJS with Google Maps. The FAQ implies that it would be in breach of the Google API ToCs but may be possible with a plugin.

      You could also look at contributing data to the OpenStreetMap project, it’s not that hard.

  5. It seems that some type of incompatibility is generated when this node is used with node-red-contrib-web-worldmap, Do you know anything about it ?, I am using node-red in IBM Bluemix

    1. If you think there is a problem, raise an issue in github. You will have to include a LOT more detail for anybody to be able to help

  6. Hi Ben,

    Thank you for your geofence node. It works nicely for me. I am wondering if you or anybody else accomplished a flow that also outputs leave/enter information.

    I have a few fenced regions defined (in parallel) and the flow continues with the matched region information if there is a match. I am struggling with adding a branch that should result with the information that the previously matched region has been left.

    I had the idea to work with setting and unsetting global variables and a few sleep functions. I would start the flow with the information that the last known region was left and overwrite that when any of known regions matches with “new region entered” data. Regrettably this isn’t really working.

    Are there any other possible approaches?

    1. Comments on Blogs really are not the best place for this sort of thing (places like the Node-RED Slack mean the whole community can help).

      Don’t use parallel filters, just chain nodes in “inarea” mode that way you can store the array in the context and check the next message against the context to see which areas have been left.

  7. Hi, I’m using this node, but what I need is multi geofences and i can’t make more than one geofence in the node, ยฟthere’s another way to make multi fences with this node?

      1. Thanks for the reply.
        But constantly the geo-fences will be added to a database and I need every time new geo-fences are added to the database in the red node as well.

        1. That’s totally different to question you originally asked. No, there is no support for dynamic geofence areas in the node. You will have to write your own.

  8. So, the only way adding a new Geofence is adding the node?
    With a msg.payload can I add a new geofence in the node?

  9. This node is amazing. Works PERFECTLY. Thank you. The only thing I am wondering about this node at this point is how can I get the geofence area to show in the node-red-contrib-web-worldmap
    node which I have displaying on my dashboard?
    I appreciate you taking the time to look at my question and eagerly await your response. ๐Ÿ™‚

    1. At the moment there is no way to export the geofence zones from the node so they could be imported into the worldmap node.

      I’ll try and have a chat with Dave (Author of the worldmap node) and see if there is something I can attach to the output message that the worldmap will understand.

      1. If it could output in a way that the worldmap’s “convex-hull” node can read then it should display perfectly fine. Unfortunately my coding skills are not good otherwise I would gladly help make this happen. For example. It can read GeoJSON in a format like this –>

        msg.payload = {
        “name”: “MyPolygon”,
        “geojson”: {
        “type”: “Feature”,
        “geometry”: {
        “type”: “Polygon”,
        “coordinates”: [[[-180,10],[20,90],[180,-5],[-30,-90]]]
        },
        “style”: {
        “stroke-width”: “8”,
        “stroke”: “#ff00ff”,
        “fill-color”: “#808000”,
        “fill-opacity”: 0.2
        }
        }
        }

        So if we could get the geofence node to be able to make an output like this then we would be good to go and the geofence node could be fed into the convex-hull node and then on to the worldmap node where it would be displayed.

        Wish I was able to help more. Thanks for your time. ๐Ÿ™‚

        1. Check out v0.2.1. It now has an option to enable a second output to feed into the WorldMap out node (not the convex-hull). It will need poking by the WorldMap in node when ever a new browsers opens the map.

          1. I have updated, restarted node-red but I am not getting anything on the output in the debug node and I am not seeing the geofence on the map.
            Am I correct in assuming that I am going straight from the geofence node into the worldmap node? (I’ve tried with both the regular worldmap node and also the “dashboard” worldmap node). I do not see the geofence in either worldmap node.
            I think it is not working because I am not getting any data on the 2nd output to a debug node (Using “Complete Message Object”).

          2. You will only get a msg on the second output when the input msg.payload is JSON with the following in:

            {
            action: "send"
            }

  10. Thank you so much for that little tip. As soon as you gave me that tip (With a bit of work/trial and error) I was able to get it figured out and it is working exactly as expected.
    I only seem to see one problem now with this new update. If you change the “Zoom” on the worldmap then the GeoFence node has an issue with redrawing itself to match the new Zoom Level. This has the GeoFence being thrown off from where it should be displayed.

    P.S. I truly appreciate you taking the time to hear my feature request and then literally add it in so very fast. And not just fast, but also working! Good Job.

  11. I’ve since discovered that it appears to have been happening because of the way I was using the geofence node. I had the node being used in multiple ways through the one node. As soon as I split this “displaying geofence” in the worldmap from what I already had working it stopped happening. Now there is just a brief delay while it repositions itself (which is normal behaviour). To test it I put them all back (multi using the node) and it started happening again. Split them up again and problem was solved. It’s not a deal breaker of any sorts. It simply means having a couple extra “duplicate” nodes. There is also a good chance it might be caused by my setup and the amount of data being fed through the node.

    Again, I truly appreciate you adding in this feature for me when I requested. I am sure it will get much use from many people.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.