Skip to content

Commit

Permalink
Add geojson map test
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippMatthes committed Apr 29, 2024
1 parent 68ec59a commit 1b8e1e6
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 25 deletions.
31 changes: 27 additions & 4 deletions monitor/map.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package monitor

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"predictor/env"
"predictor/log"
"predictor/predictions"
Expand All @@ -11,13 +14,19 @@ import (
geojson "github.com/paulmach/go.geojson"
)

// Interface to overwrite for testing purposes.
var getAllThings = things.Things.Range

// Interface to overwrite for testing purposes.
var getCurrentPrediction = predictions.GetCurrentPrediction

// Write geojson data that can be used to visualize the predictions.
// The geojson file is written to the static directory.
func WriteGeoJSONMap() {
// Write the geojson to the file.
locationFeatureCollection := geojson.NewFeatureCollection() // Locations of traffic lights.
laneFeatureCollection := geojson.NewFeatureCollection() // Lanes of traffic lights.
things.Things.Range(func(key, value interface{}) bool {
getAllThings(func(key, value interface{}) bool {
thingName := key.(string)
thing := value.(things.Thing)

Expand All @@ -30,7 +39,7 @@ func WriteGeoJSONMap() {
lat, lng := coordinate[1], coordinate[0]

// Check if there is a prediction for this thing.
prediction, predictionOk := predictions.GetCurrentPrediction(thingName)
prediction, predictionOk := getCurrentPrediction(thingName)
// Build the properties.
properties := make(map[string]interface{})
if predictionOk {
Expand Down Expand Up @@ -62,19 +71,33 @@ func WriteGeoJSONMap() {
return true
})

// Make sure the directory exists, otherwise create it.
locationsFilePath := fmt.Sprintf("%s/status/predictions-locations.geojson", env.StaticPath)
err := os.MkdirAll(filepath.Dir(locationsFilePath), os.ModePerm)
if err != nil {
log.Error.Println("Error creating dirs for geojson:", err)
return
}
locationsGeoJson, err := locationFeatureCollection.MarshalJSON()
if err != nil {
log.Error.Println("Error marshalling geojson:", err)
return
}
ioutil.WriteFile(env.StaticPath+"/status/predictions-locations.geojson", locationsGeoJson, 0644)
ioutil.WriteFile(locationsFilePath, locationsGeoJson, 0644)

// Make sure the directory exists, otherwise create it.
lanesFilePath := fmt.Sprintf("%s/status/predictions-lanes.geojson", env.StaticPath)
err = os.MkdirAll(filepath.Dir(lanesFilePath), os.ModePerm)
if err != nil {
log.Error.Println("Error creating dirs for geojson:", err)
return
}
lanesGeoJson, err := laneFeatureCollection.MarshalJSON()
if err != nil {
log.Error.Println("Error marshalling geojson:", err)
return
}
ioutil.WriteFile(env.StaticPath+"/status/predictions-lanes.geojson", lanesGeoJson, 0644)
ioutil.WriteFile(lanesFilePath, lanesGeoJson, 0644)
}

func UpdateGeoJSONMapPeriodically() {
Expand Down
138 changes: 138 additions & 0 deletions monitor/map_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package monitor

import (
"fmt"
"os"
"predictor/env"
"predictor/predictions"
"predictor/things"
"testing"
"time"

geojson "github.com/paulmach/go.geojson"
)

func TestWriteGeoJSONMap(t *testing.T) {
laneTopology := things.LocationMultiLineString{
Type: "MultiLineString",
// Mock values
Coordinates: [][][]float64{
// Ingress lane
{
{
0, 0,
},
{
1, 0,
},
},
// Connection lane
{
{
1, 0,
},
{
2, 0,
},
},
// Egress lane
{
{
2, 0,
},
{
3, 0,
},
},
},
}
getAllThings = func(callback func(key, value interface{}) bool) {
callback(
"1337_1", things.Thing{
Name: "1337_1",
Properties: things.ThingProperties{
LaneType: "Radfahrer",
},
Locations: []things.Location{
{
Location: things.LocationGeoJson{
Geometry: laneTopology,
},
},
},
},
)
}
mockPrediction := predictions.Prediction{
ThingName: "1337_1",
Now: []byte{1, 1, 1, 1, 1, 3, 3, 3, 3, 3},
NowQuality: []byte{100, 100, 100, 100, 100, 100, 100, 100, 100, 100},
Then: []byte{1, 1, 1, 1, 1, 3, 3, 3, 3, 3},
ThenQuality: []byte{100, 100, 100, 100, 100, 100, 100, 100, 100, 100},
ReferenceTime: time.Unix(0, 0),
}
getCurrentPrediction = func(thingName string) (predictions.Prediction, bool) {
return mockPrediction, true
}

tempDir := t.TempDir()
env.StaticPath = tempDir

locationsGeoJSONFilePath := fmt.Sprintf("%s/status/predictions-locations.geojson", tempDir)
lanesGeoJSONFilePath := fmt.Sprintf("%s/status/predictions-lanes.geojson", tempDir)

WriteGeoJSONMap()

locationsData, err := os.ReadFile(locationsGeoJSONFilePath)
if err != nil {
t.Errorf("failed to read locations geojson file")
t.FailNow()
}
lanesData, err := os.ReadFile(lanesGeoJSONFilePath)
if err != nil {
t.Errorf("failed to read lanes geojson file")
t.FailNow()
}
locationsGeoJSON, err := geojson.UnmarshalFeatureCollection(locationsData)
if err != nil {
t.Errorf("failed to unmarshal geojson feature collection")
t.FailNow()
}
lanesGeoJSON, err := geojson.UnmarshalFeatureCollection(lanesData)
if err != nil {
t.Errorf("failed to unmarshal geojson feature collection")
}
if len(locationsGeoJSON.Features) != 1 || len(lanesGeoJSON.Features) != 1 {
t.Errorf("more or less than one geojson feature detected")
t.FailNow()
}
unmarshaledLocationFeature := locationsGeoJSON.Features[0]
unmarshaledLaneFeature := lanesGeoJSON.Features[0]

type checker func(v interface{}) bool
propertyChecks := map[string]checker{
"prediction_available": func(v interface{}) bool {
return v.(bool) == true
},
"prediction_quality": func(v interface{}) bool {
return v.(float64) == 1.0
},
"prediction_time_diff": func(v interface{}) bool {
return v.(float64) > 0
},
"prediction_sg_id": func(v interface{}) bool {
return v.(string) == "1337_1"
},
}

for key, check := range propertyChecks {
v := unmarshaledLocationFeature.Properties[key]
if !check(v) {
t.Errorf("property check failed: %v", v)
}
v = unmarshaledLaneFeature.Properties[key]
if !check(v) {
t.Errorf("property check failed: %v", v)
}
}
}
28 changes: 16 additions & 12 deletions things/location.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ package things

// A location model from the SensorThings API.
type Location struct {
Description string `json:"description"`
EncodingType string `json:"encodingType"`
IotId int `json:"@iot.id"`
Location struct { // GeoJSON
Type string `json:"type"`
Geometry struct {
Type string `json:"type"` // MultiLineString
Coordinates [][][]float64 `json:"coordinates"`
} `json:"geometry"`
Name string `json:"name"`
SelfLink string `json:"@iot.selfLink"`
} `json:"location"`
Description string `json:"description"`
EncodingType string `json:"encodingType"`
IotId int `json:"@iot.id"`
Location LocationGeoJson `json:"location"`
}

type LocationGeoJson struct {
Type string `json:"type"`
Geometry LocationMultiLineString `json:"geometry"`
Name string `json:"name"`
SelfLink string `json:"@iot.selfLink"`
}

type LocationMultiLineString struct {
Type string `json:"type"` // MultiLineString
Coordinates [][][]float64 `json:"coordinates"`
}
20 changes: 11 additions & 9 deletions things/thing.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ import "fmt"

// A traffic light thing from the SensorThings API.
type Thing struct {
IotId int `json:"@iot.id"`
Name string `json:"name"`
Description string `json:"description"`
Properties struct {
LaneType string `json:"laneType"`
TrafficLightsId string `json:"trafficLightsId"` // This is the crossing.
} `json:"properties"`
Datastreams []Datastream `json:"Datastreams"`
Locations []Location `json:"Locations"`
IotId int `json:"@iot.id"`
Name string `json:"name"`
Description string `json:"description"`
Properties ThingProperties `json:"properties"`
Datastreams []Datastream `json:"Datastreams"`
Locations []Location `json:"Locations"`
}

type ThingProperties struct {
LaneType string `json:"laneType"`
TrafficLightsId string `json:"trafficLightsId"` // This is the crossing.
}

// Get the lane of a thing. This is the connection lane of the thing.
Expand Down

0 comments on commit 1b8e1e6

Please sign in to comment.