Skip to content

Commit

Permalink
[TASK] Add output for geojson nodes only
Browse files Browse the repository at this point in the history
PR: #164
  • Loading branch information
krombel authored and genofire committed May 21, 2019
1 parent 3126785 commit 80d4243
Show file tree
Hide file tree
Showing 8 changed files with 324 additions and 1 deletion.
2 changes: 1 addition & 1 deletion cmd/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestReadConfig(t *testing.T) {
assert.Contains(config.Respondd.Sites["ffhb"].Domains, "city")

// Test output plugins
assert.Len(config.Nodes.Output, 3)
assert.Len(config.Nodes.Output, 4)
outputs := config.Nodes.Output["meshviewer"].([]interface{})
assert.Len(outputs, 1)
meshviewer := outputs[0]
Expand Down
5 changes: 5 additions & 0 deletions config_example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ offline_after = "10m"
#longitude_max = 39.72


# outputs all nodes as points into nodes.geojson
[[nodes.output.geojson]]
enable = true
path = "/var/www/html/meshviewer/data/nodes.geojson"

# definition for the new more compressed meshviewer.json
[[nodes.output.meshviewer-ffrgb]]
enable = true
Expand Down
25 changes: 25 additions & 0 deletions docs/docs_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,31 @@ longitude_max = 39.72



## [[nodes.output.geojson]]
{% method %}
The geojson output produces a geojson file which contains the location data of all monitored nodes to be used to visualize the location of the nodes.
It is optimized to be used with [UMap](https://github.com/umap-project/umap) but should work with other tools as well.
Here is a public demo provided by Freifunk Muenchen: http://u.osmfr.org/m/328494/
{% sample lang="toml" %}
```toml
[[nodes.output.geojson]]
enable = true
path = "/var/www/html/meshviewer/data/nodes.geojson"
```
{% endmethod %}


### path
{% method %}
The path, where to store nodes.geojson
{% sample lang="toml" %}
```toml
path = "/var/www/html/meshviewer/data/nodes.geojson"
```
{% endmethod %}



## [[nodes.output.meshviewer-ffrgb]]
{% method %}
The new json file format for the [meshviewer](https://github.com/ffrgb/meshviewer) developed in Regensburg.
Expand Down
1 change: 1 addition & 0 deletions output/all/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package all

import (
_ "github.com/FreifunkBremen/yanic/output/geojson"
_ "github.com/FreifunkBremen/yanic/output/meshviewer"
_ "github.com/FreifunkBremen/yanic/output/meshviewer-ffrgb"
_ "github.com/FreifunkBremen/yanic/output/nodelist"
Expand Down
89 changes: 89 additions & 0 deletions output/geojson/geojson.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package geojson

import (
"strconv"
"strings"

"github.com/FreifunkBremen/yanic/runtime"
"github.com/paulmach/go.geojson"
)

const (
POINT_UMAP_CLASS = "Circle"
POINT_UMAP_ONLINE_COLOR = "Green"
POINT_UMAP_OFFLINE_COLOR = "Red"
)

func newNodePoint(n *runtime.Node) (point *geojson.Feature) {
nodeinfo := n.Nodeinfo
location := nodeinfo.Location
point = geojson.NewPointFeature([]float64{
location.Longitude,
location.Latitude,
})
point.Properties["id"] = nodeinfo.NodeID
point.Properties["name"] = nodeinfo.Hostname

point.Properties["online"] = n.Online
var description strings.Builder
if n.Online {
description.WriteString("Online\n")
if statistics := n.Statistics; statistics != nil {
point.Properties["clients"] = statistics.Clients.Total
description.WriteString("Clients: " + strconv.Itoa(int(statistics.Clients.Total)) + "\n")
}
} else {
description.WriteString("Offline\n")
}
if nodeinfo.Hardware.Model != "" {
point.Properties["model"] = nodeinfo.Hardware.Model
description.WriteString("Model: " + nodeinfo.Hardware.Model + "\n")
}
if fw := nodeinfo.Software.Firmware; fw.Release != "" {
point.Properties["firmware"] = fw.Release
description.WriteString("Firmware: " + fw.Release + "\n")
}
if nodeinfo.System.SiteCode != "" {
point.Properties["site"] = nodeinfo.System.SiteCode
description.WriteString("Site: " + nodeinfo.System.SiteCode + "\n")
}
if nodeinfo.System.DomainCode != "" {
point.Properties["domain"] = nodeinfo.System.DomainCode
description.WriteString("Domain: " + nodeinfo.System.DomainCode + "\n")
}
if owner := nodeinfo.Owner; owner != nil && owner.Contact != "" {
point.Properties["contact"] = owner.Contact
description.WriteString("Contact: " + owner.Contact + "\n")
}

point.Properties["description"] = description.String()
point.Properties["_umap_options"] = getUMapOptions(n)
return
}

func getUMapOptions(n *runtime.Node) map[string]string {
result := map[string]string{
"iconClass": POINT_UMAP_CLASS,
}
if n.Online {
result["color"] = POINT_UMAP_ONLINE_COLOR
} else {
result["color"] = POINT_UMAP_OFFLINE_COLOR
}
return result
}

func transform(nodes *runtime.Nodes) *geojson.FeatureCollection {
nodelist := geojson.NewFeatureCollection()

for _, n := range nodes.List {
if n.Nodeinfo == nil || n.Nodeinfo.Location == nil {
continue
}
point := newNodePoint(n)
if point != nil {
nodelist.Features = append(nodelist.Features, point)
}
}
return nodelist
}
129 changes: 129 additions & 0 deletions output/geojson/geojson_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package geojson

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/FreifunkBremen/yanic/data"
"github.com/FreifunkBremen/yanic/runtime"
)

const (
testNodeDescription string = "Online\nClients: 42\nModel: TP-Link 841\n" +
"Site: mysite\nDomain: domain_42\n"
)

func TestTransform(t *testing.T) {
testNodes := createTestNodes()
nodes := transform(testNodes)

assert := assert.New(t)
assert.Len(testNodes.List, 4)
assert.Len(nodes.Features, 3)

node := testNodes.List["abcdef012425"]

umap := getUMapOptions(node)
assert.Len(umap, 2)

nodePoint := newNodePoint(node)
assert.Equal(
"abcdef012425",
nodePoint.Properties["id"],
)
assert.Equal(
"TP-Link 841",
nodePoint.Properties["model"],
)
assert.Equal(
uint32(42),
nodePoint.Properties["clients"],
)
assert.Equal(
testNodeDescription,
nodePoint.Properties["description"],
)
}

func createTestNodes() *runtime.Nodes {
nodes := runtime.NewNodes(&runtime.NodesConfig{})

nodes.AddNode(&runtime.Node{
Online: true,
Statistics: &data.Statistics{
Clients: data.Clients{
Total: 42,
},
},
Nodeinfo: &data.Nodeinfo{
NodeID: "abcdef012425",
Hardware: data.Hardware{
Model: "TP-Link 841",
},
Location: &data.Location{
Latitude: 24,
Longitude: 2,
},
System: data.System{
SiteCode: "mysite",
DomainCode: "domain_42",
},
},
})

nodeData := &runtime.Node{
Online: true,
Statistics: &data.Statistics{
Clients: data.Clients{
Total: 23,
},
},
Nodeinfo: &data.Nodeinfo{
NodeID: "abcdef012345",
Hardware: data.Hardware{
Model: "TP-Link 842",
},
System: data.System{
SiteCode: "mysite",
DomainCode: "domain_42",
},
},
}
nodeData.Nodeinfo.Software.Firmware.Release = "2019.1~exp42"
nodes.AddNode(nodeData)

nodes.AddNode(&runtime.Node{
Statistics: &data.Statistics{
Clients: data.Clients{
Total: 2,
},
},
Nodeinfo: &data.Nodeinfo{
NodeID: "112233445566",
Hardware: data.Hardware{
Model: "TP-Link 843",
},
Location: &data.Location{
Latitude: 23,
Longitude: 2,
},
},
})

nodes.AddNode(&runtime.Node{
Nodeinfo: &data.Nodeinfo{
NodeID: "0xdeadbeef0x",
VPN: true,
Hardware: data.Hardware{
Model: "Xeon Multi-Core",
},
Location: &data.Location{
Latitude: 23,
Longitude: 22,
},
},
})

return nodes
}
46 changes: 46 additions & 0 deletions output/geojson/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package geojson

import (
"errors"

"github.com/FreifunkBremen/yanic/output"
"github.com/FreifunkBremen/yanic/runtime"
)

type Output struct {
output.Output
path string
}

type Config map[string]interface{}

func (c Config) Path() string {
if path, ok := c["path"]; ok {
return path.(string)
}
return ""
}

func init() {
output.RegisterAdapter("geojson", Register)
}

func Register(configuration map[string]interface{}) (output.Output, error) {
var config Config
config = configuration

if path := config.Path(); path != "" {
return &Output{
path: path,
}, nil
}
return nil, errors.New("no path given")

}

func (o *Output) Save(nodes *runtime.Nodes) {
nodes.RLock()
defer nodes.RUnlock()

runtime.SaveJSON(transform(nodes), o.path)
}
28 changes: 28 additions & 0 deletions output/geojson/output_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package geojson

import (
"os"
"testing"

"github.com/FreifunkBremen/yanic/runtime"
"github.com/stretchr/testify/assert"
)

func TestOutput(t *testing.T) {
assert := assert.New(t)

out, err := Register(map[string]interface{}{})
assert.Error(err)
assert.Nil(out)

out, err = Register(map[string]interface{}{
"path": "/tmp/nodes.geojson",
})
os.Remove("/tmp/nodes.geojson")
assert.NoError(err)
assert.NotNil(out)

out.Save(&runtime.Nodes{})
_, err = os.Stat("/tmp/nodes.geojson")
assert.NoError(err)
}

0 comments on commit 80d4243

Please sign in to comment.