Skip to content

Commit

Permalink
feat: implement connectivitymap.
Browse files Browse the repository at this point in the history
issue: #238
  • Loading branch information
CodeBear801 committed Apr 1, 2020
1 parent 05261ba commit f430853
Show file tree
Hide file tree
Showing 12 changed files with 588 additions and 8 deletions.
65 changes: 65 additions & 0 deletions integration/service/connectivitymap/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package connectivitymap

import "github.com/Telenav/osrm-backend/integration/service/spatialindexer"

type connectivityMapBuilder struct {
iterator spatialindexer.PointsIterator
finder spatialindexer.Finder
ranker spatialindexer.Ranker
distanceLimit float64
}

func newConnectivityMapBuilder(iterator spatialindexer.PointsIterator, finder spatialindexer.Finder,
ranker spatialindexer.Ranker, distanceLimit float64) *connectivityMapBuilder {
return &connectivityMapBuilder{
iterator: iterator,
finder: finder,
ranker: ranker,
distanceLimit: distanceLimit,
}
}

type placeIDWithNearByPlaceIDs struct {
id spatialindexer.PointID
ids []IDAndDistance
}

// todo: use task pool
func (builder *connectivityMapBuilder) build() ID2NearByIDsMap {
internalResult := make(chan placeIDWithNearByPlaceIDs, 10000)
m := make(ID2NearByIDsMap)

go func() {
for p := range builder.iterator.IteratePoints() {
nearbyIDs := builder.finder.FindNearByPointIDs(p.Location, builder.distanceLimit, spatialindexer.UnlimitedCount)
rankedResults := builder.ranker.RankPointIDsByGreatCircleDistance(p.Location, nearbyIDs)

ids := make([]IDAndDistance, 0, len(rankedResults))
for _, r := range rankedResults {
ids = append(ids, IDAndDistance{
ID: r.ID,
Distance: r.Distance,
})
}
internalResult <- placeIDWithNearByPlaceIDs{
id: p.ID,
ids: ids,
}
}
close(internalResult)
}()

for item := range internalResult {
m[item.id] = item.ids
}

return m
}

// func (builder *connectivityMapBuilder) dump(folderPath string, m ID2NearByIDsMap) {

// }

// func (builder *connectivityMapBuilder) load(folderPath string) (ID2NearByIDsMap, float64) {

// }
46 changes: 46 additions & 0 deletions integration/service/connectivitymap/connectivity_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package connectivitymap

import (
"github.com/Telenav/osrm-backend/integration/service/spatialindexer"
"github.com/golang/glog"
)

// IDAndDistance wraps ID and distance information
type IDAndDistance struct {
ID spatialindexer.PointID
Distance float64
}

// ID2NearByIDsMap is a mapping between ID and its nearby IDs
type ID2NearByIDsMap map[spatialindexer.PointID][]IDAndDistance

type ConnectivityMap struct {
id2nearByIDs ID2NearByIDsMap
distanceLimitation float64
statistic *statistic
}

func (cm *ConnectivityMap) DistanceLimitation() float64 {
return cm.distanceLimitation
}

func NewPlaceConnectivityMap(distanceLimitation float64) *ConnectivityMap {
return &ConnectivityMap{
distanceLimitation: distanceLimitation,
statistic: newStatistic(),
}
}

func (cm *ConnectivityMap) Build() {
glog.Info("Successfully finished GenerateConnectivityMap\n")
}

func (cm *ConnectivityMap) Dump(folderPath string) {
}

func (cm *ConnectivityMap) Load(folderPath string) {
}

func (cm *ConnectivityMap) QueryConnectivity(placeInfo spatialindexer.PointInfo, limitDistance float64) {
// for each everything recorded in data, apply limit option on that
}
66 changes: 66 additions & 0 deletions integration/service/connectivitymap/connectivity_map_mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package connectivitymap

var fakeID2NearByIDsMap1 = ID2NearByIDsMap{
1: []IDAndDistance{
IDAndDistance{
ID: 2,
Distance: 3,
},
IDAndDistance{
ID: 5,
Distance: 4,
},
IDAndDistance{
ID: 7,
Distance: 6,
},
IDAndDistance{
ID: 8,
Distance: 12,
},
},

2: []IDAndDistance{
IDAndDistance{
ID: 1,
Distance: 3,
},
IDAndDistance{
ID: 7,
Distance: 23,
},
},

5: []IDAndDistance{
IDAndDistance{
ID: 1,
Distance: 4,
},
IDAndDistance{
ID: 8,
Distance: 5,
},
},

7: []IDAndDistance{
IDAndDistance{
ID: 1,
Distance: 6,
},
IDAndDistance{
ID: 2,
Distance: 23,
},
},

8: []IDAndDistance{
IDAndDistance{
ID: 5,
Distance: 5,
},
IDAndDistance{
ID: 1,
Distance: 12,
},
},
}
32 changes: 32 additions & 0 deletions integration/service/connectivitymap/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
package placeconnectivitymap provides connectivity information for place graphs.
For example, given following graph
- - - - 4
| | | | |
- 2 - - -
| | | | |
- - - 3 -
| | | | |
1 - - - -
Place connectivity map will pre-process each points and generate following result:
While query connectivity for place 1, will return
(place 2, 3), // the shortest path between place 1 and place 2 is 3
(place 3, 4), // the shortest path between place 1 and place 2 is 4
(place 4, 7), // the shortest path between place 1 and place 2 is 7
The result is sorted by shortest path distance(or other user defined strategy)
When query for connectivity, user could also pass in limitation option, such as distance limitation.
For example, when query connectivity for place 3
With limitation = -1, it will return
(place 2, 3), // the shortest path between place 3 and place 2 is 3
(place 4, 3), // the shortest path between place 3 and place 4 is 3
(place 1, 4), // the shortest path between place 3 and place 1 is 4
With limitation = 3, it will return
(place 2, 3), // the shortest path between place 3 and place 2 is 3
(place 4, 3), // the shortest path between place 3 and place 4 is 3
*/
package connectivitymap
118 changes: 118 additions & 0 deletions integration/service/connectivitymap/dumper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package connectivitymap

import (
"bytes"
"encoding/gob"
"io/ioutil"
"os"
"strings"

"github.com/Telenav/osrm-backend/integration/pkg/api"
"github.com/golang/glog"
)

const id2NearByIDsMapFileName = "id2nearbyidsmap.gob"

func serializeConnectivityMap(cm *ConnectivityMap, folderPath string) error {
if !strings.HasSuffix(folderPath, api.Slash) {
folderPath += api.Slash
}

if err := serializeID2NearByIDsMap(cm, folderPath); err != nil {
return err
}

if err := cm.statistic.dump(folderPath); err != nil {
return err
}

glog.Infof("Successfully deserialize ConnectivityMap from folder %s.\n", folderPath)

return nil
}

func deSerializeConnectivityMap(cm *ConnectivityMap, folderPath string) error {
if !strings.HasSuffix(folderPath, api.Slash) {
folderPath += api.Slash
}

if err := deSerializeID2NearByIDsMap(cm, folderPath); err != nil {
return err
}

if err := cm.statistic.load(folderPath); err != nil {
return err
}
cm.distanceLimitation = cm.statistic.DistanceLimitation

return nil
}

func removeAllDumpFiles(folderPath string) error {
if !strings.HasSuffix(folderPath, api.Slash) {
folderPath += "/"
}

_, err := os.Stat(folderPath + id2NearByIDsMapFileName)
if !os.IsNotExist(err) {
err = os.Remove(folderPath + id2NearByIDsMapFileName)
if err != nil {
glog.Errorf("Remove file failed %s\n", folderPath+id2NearByIDsMapFileName)
return err
}
} else {
glog.Warningf("There is no %s file in folder %s\n", id2NearByIDsMapFileName, folderPath)
}

_, err = os.Stat(folderPath + statisticFileName)
if !os.IsNotExist(err) {
err = os.Remove(folderPath + statisticFileName)
if err != nil {
glog.Errorf("Remove file failed %s\n", folderPath+statisticFileName)
return err
}
} else {
glog.Warningf("There is no %s file in folder %s\n", statisticFileName, folderPath)
}

return nil
}

func serializeID2NearByIDsMap(cm *ConnectivityMap, folderPath string) error {
buf := new(bytes.Buffer)
encoder := gob.NewEncoder(buf)
err := encoder.Encode(cm.id2nearByIDs)

if err != nil {
glog.Errorf("During encode ConnectivityMap's ID2NearByIDsMap met error %v", err)
return err
}

if err = ioutil.WriteFile(folderPath+id2NearByIDsMapFileName, buf.Bytes(), 0644); err != nil {
glog.Errorf("During dump ConnectivityMap's ID2NearByIDsMap to %s met error %v", folderPath+id2NearByIDsMapFileName, err)
return err
}

return nil
}

func deSerializeID2NearByIDsMap(cm *ConnectivityMap, folderPath string) error {
byteArray, err := ioutil.ReadFile(folderPath + id2NearByIDsMapFileName)
if err != nil {
glog.Errorf("During load ConnectivityMap's ID2NearByIDsMap from %s met error %v",
folderPath+id2NearByIDsMapFileName, err)
return err
}

buf := bytes.NewBuffer(byteArray)
decoder := gob.NewDecoder(buf)
err = decoder.Decode(&cm.id2nearByIDs)

if err != nil {
glog.Errorf("During decode ConnectivityMap's ID2NearByIDsMap from %s met error %v",
folderPath+id2NearByIDsMapFileName, err)
return err
}

return nil
}
60 changes: 60 additions & 0 deletions integration/service/connectivitymap/dumper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package connectivitymap

import (
"os"
"reflect"
"testing"

"github.com/golang/glog"
)

func TestDumper(t *testing.T) {
cases := []ConnectivityMap{
ConnectivityMap{
id2nearByIDs: fakeID2NearByIDsMap1,
distanceLimitation: fakeDistanceLimit,
statistic: &fakeStatisticResult1,
},
}

// check whether curent folder is writeable
path, _ := os.Getwd()
_, err := os.OpenFile(path+"/tmp", os.O_RDONLY|os.O_CREATE, 0644)
if err != nil {
return
}
if err := os.Remove(path + "/tmp"); err != nil {
glog.Errorf("During dumper test, remove path %s failed.\n", path+"/tmp")
return
}

if err := removeAllDumpFiles(path); err != nil {
t.Errorf("During running removeAllDumpFiles met error %v", err)
}
for _, c := range cases {
if err := serializeConnectivityMap(&c, path); err != nil {
t.Errorf("During running serializeConnectivityMap for case %v, met error %v", c, err)
}

actual := NewPlaceConnectivityMap(0.0)
if err := deSerializeConnectivityMap(actual, path); err != nil {
t.Errorf("During running deSerializeConnectivityMap for case %v, met error %v", c, err)
}

if !reflect.DeepEqual(actual.id2nearByIDs, c.id2nearByIDs) {
t.Errorf("Expect result \n%+v but got \n%+v\n", c.id2nearByIDs, actual.id2nearByIDs)
}

if !reflect.DeepEqual(actual.distanceLimitation, c.distanceLimitation) {
t.Errorf("Expect result \n%+v but got \n%+v\n", c.distanceLimitation, actual.distanceLimitation)
}

if !reflect.DeepEqual(actual.statistic, c.statistic) {
t.Errorf("Expect result \n%+v but got \n%+v\n", c.statistic, actual.statistic)
}

if err := removeAllDumpFiles(path); err != nil {
t.Errorf("During running removeAllDumpFiles met error %v", err)
}
}
}
Loading

0 comments on commit f430853

Please sign in to comment.