Skip to content

Commit

Permalink
feat: implement station connectivity graph based on pre-build data
Browse files Browse the repository at this point in the history
issue: #242
  • Loading branch information
CodeBear801 committed May 4, 2020
1 parent 9812703 commit a4c42e7
Show file tree
Hide file tree
Showing 7 changed files with 464 additions and 4 deletions.
10 changes: 7 additions & 3 deletions integration/service/oasis/connectivitymap/connectivity_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,13 @@ func (cm *ConnectivityMap) Load(folderPath string) *ConnectivityMap {
return cm
}

// QueryConnectivity answers connectivity query for given placeInfo
func (cm *ConnectivityMap) QueryConnectivity(placeInfo spatialindexer.PointInfo, limitDistance float64) {
// for each everything recorded in data, apply limit option on that
// QueryConnectivity answers connectivity query for given placeID
// Return true and IDAndDistance array for given placeID, otherwise false and nil
func (cm *ConnectivityMap) QueryConnectivity(placeID spatialindexer.PointID) ([]IDAndDistance, bool) {
if result, ok := cm.id2nearByIDs[placeID]; ok {
return result, true
}
return nil, false
}

// MaxRange tells the value used to pre-process place data.
Expand Down
20 changes: 20 additions & 0 deletions integration/service/oasis/connectivitymap/connectivity_map_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,23 @@ var fakeID2NearByIDsMap1 = ID2NearByIDsMap{
},
},
}

var fakeID2NearByIDsMap2 = ID2NearByIDsMap{
1: []IDAndDistance{
{
ID: 2,
Distance: 1,
},
},
2: []IDAndDistance{
{
ID: 3,
Distance: 2,
},
},
}

// MockConnectivityMap constructs simple connectivity map for integration test
var MockConnectivityMap = ConnectivityMap{
id2nearByIDs: fakeID2NearByIDsMap2,
}
18 changes: 17 additions & 1 deletion integration/service/oasis/spatialindexer/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package spatialindexer

import (
"math"
"strconv"

"github.com/Telenav/osrm-backend/integration/api/nav"
)

// Location for poi point
// @todo: will be replaced by the one in map
// todo codebear801 will be replaced by the one in nav
type Location struct {
Lat float64
Lon float64
Expand All @@ -27,6 +30,11 @@ type RankedPointInfo struct {
// Only the data used for pre-processing contains valid PointID
type PointID int64

// String converts PointID to string
func (p PointID) String() string {
return strconv.FormatInt((int64)(p), 10)
}

// UnlimitedCount means all spatial search result will be returned
const UnlimitedCount = math.MaxInt32

Expand All @@ -47,6 +55,14 @@ type Ranker interface {
RankPointIDsByShortestDistance(center Location, targets []*PointInfo) []*RankedPointInfo
}

// PlaceLocationQuerier returns *nav.location for given location
type PlaceLocationQuerier interface {

// GetLocation returns *nav.Location for given placeID
// Returns nil if given placeID is not found
GetLocation(placeID string) *nav.Location
}

// PointsIterator provides iterateability for PointInfo
type PointsIterator interface {

Expand Down
21 changes: 21 additions & 0 deletions integration/service/oasis/spatialindexer/s2indexer/indexer.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package s2indexer

import (
"strconv"
"time"

"github.com/Telenav/osrm-backend/integration/api/nav"
"github.com/Telenav/osrm-backend/integration/service/oasis/spatialindexer"
"github.com/Telenav/osrm-backend/integration/service/oasis/spatialindexer/poiloader"
"github.com/golang/geo/s2"
Expand Down Expand Up @@ -118,6 +120,25 @@ func (indexer *S2Indexer) FindNearByPointIDs(center spatialindexer.Location, rad
return results
}

// GetLocation returns *nav.Location for given placeID
// Returns nil if given placeID is not found
func (indexer *S2Indexer) GetLocation(placeID string) *nav.Location {
id, err := strconv.Atoi(placeID)
if err != nil {
glog.Errorf("Incorrect station ID passed to NearByStationQuery %+v, got error %#v", placeID, err)
return nil
}
if location, ok := indexer.pointID2Location[(spatialindexer.PointID)(id)]; ok {
return &nav.Location{
Lat: location.Lat,
Lon: location.Lon,
}
}

return nil
}

//TODO codebear801 This function should be replaced by GetLocation
func (indexer S2Indexer) getPointLocationByPointID(id spatialindexer.PointID) (spatialindexer.Location, bool) {
location, ok := indexer.pointID2Location[id]
return location, ok
Expand Down
9 changes: 9 additions & 0 deletions integration/service/oasis/stationconnquerier/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
Package stationconnquerier provides connectivitymap.Querier interface based on pre-build connectivity data.
It needs to connect orig and destination point into station graph.
For orig/start point, based on electric vehicle's current energy level, it queries all possible reachable stations take start point as center.
For destination/end point, based on electric vehicle's max energy level, it queries all possible stations which could reach destination with maximum amount of charge.
For charge stations, it retrieves connectivity from pre-build data. If a charge station is reachable to destination/end point, it must connects that into graph.
*/
package stationconnquerier
151 changes: 151 additions & 0 deletions integration/service/oasis/stationconnquerier/station_conn_querier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package stationconnquerier

import (
"strconv"

"github.com/Telenav/osrm-backend/integration/api/nav"
"github.com/Telenav/osrm-backend/integration/service/oasis/connectivitymap"
"github.com/Telenav/osrm-backend/integration/service/oasis/spatialindexer"
"github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder/stationfindertype"
"github.com/golang/glog"
)

type StationConnectivityQuerier struct {
stationLocationQuerier spatialindexer.PlaceLocationQuerier
stationConnectivity *connectivitymap.ConnectivityMap
reachableStationsByStart []*connectivitymap.QueryResult
reachableStationToEnd map[string]*connectivitymap.QueryResult
startLocation *nav.Location
endLocation *nav.Location
}

func New(stationFinder spatialindexer.Finder, stationRanker spatialindexer.Ranker,
stationLocationQuerier spatialindexer.PlaceLocationQuerier,
stationConnectivity *connectivitymap.ConnectivityMap,
start, end *nav.Location,
currEnergyLevel, maxEnergyLevel float64) connectivitymap.Querier {

querier := &StationConnectivityQuerier{
stationLocationQuerier: stationLocationQuerier,
stationConnectivity: stationConnectivity,
startLocation: start,
endLocation: end,
}
querier.connectStartIntoStationGraph(stationFinder, stationRanker, start, currEnergyLevel)
querier.connectEndIntoStationGraph(stationFinder, stationRanker, end, maxEnergyLevel)

return querier
}

func (querier *StationConnectivityQuerier) connectStartIntoStationGraph(stationFinder spatialindexer.Finder, stationRanker spatialindexer.Ranker,
start *nav.Location, currEnergyLevel float64) {
center := spatialindexer.Location{Lat: start.Lat, Lon: start.Lon}
nearByPoints := stationFinder.FindNearByPointIDs(center, currEnergyLevel, spatialindexer.UnlimitedCount)
rankedPoints := stationRanker.RankPointIDsByShortestDistance(center, nearByPoints)

reachableStationsByStart := make([]*connectivitymap.QueryResult, 0, len(rankedPoints))
for _, rankedPointInfo := range rankedPoints {
tmp := &connectivitymap.QueryResult{
StationID: rankedPointInfo.ID.String(),
StationLocation: &nav.Location{Lat: rankedPointInfo.Location.Lat, Lon: rankedPointInfo.Location.Lon},
Distance: rankedPointInfo.Distance,
// TODO codebear801 Replace with pre-calculate duration https://github.com/Telenav/osrm-backend/issues/321
Duration: rankedPointInfo.Distance,
}
reachableStationsByStart = append(reachableStationsByStart, tmp)
}

querier.reachableStationsByStart = reachableStationsByStart
}

func (querier *StationConnectivityQuerier) connectEndIntoStationGraph(stationFinder spatialindexer.Finder, stationRanker spatialindexer.Ranker,
end *nav.Location, maxEnergyLevel float64) {
center := spatialindexer.Location{Lat: end.Lat, Lon: end.Lon}
nearByPoints := stationFinder.FindNearByPointIDs(center, maxEnergyLevel, spatialindexer.UnlimitedCount)
rankedPoints := stationRanker.RankPointIDsByShortestDistance(center, nearByPoints)

reachableStationToEnd := make(map[string]*connectivitymap.QueryResult)
for _, rankedPointInfo := range rankedPoints {
reachableStationToEnd[rankedPointInfo.ID.String()] = &connectivitymap.QueryResult{
StationID: stationfindertype.DestLocationID,
StationLocation: end,
Distance: rankedPointInfo.Distance,
//TODO codebear801 https://github.com/Telenav/osrm-backend/issues/321
Duration: rankedPointInfo.Distance,
}
}

querier.reachableStationToEnd = reachableStationToEnd
}

// NearByStationQuery finds near by stations by given stationID and return them in recorded sequence
// Returns nil if given stationID is not found or no connectivity
func (querier *StationConnectivityQuerier) NearByStationQuery(stationID string) []*connectivitymap.QueryResult {

if stationID == stationfindertype.OrigLocationID {
return querier.reachableStationsByStart
}

if stationID == stationfindertype.DestLocationID {
return nil
}

placeID, err := strconv.Atoi(stationID)
if err != nil {
glog.Errorf("Incorrect station ID passed to NearByStationQuery %+v, got error %#v", stationID, err)
return nil
}
if connectivityResults, ok := querier.stationConnectivity.QueryConnectivity((spatialindexer.PointID)(placeID)); ok {
size := len(connectivityResults)
if querier.isStationConnectsToEnd(stationID) {
size += 1
}

results := make([]*connectivitymap.QueryResult, 0, size)
for _, idAndWeight := range connectivityResults {
tmp := &connectivitymap.QueryResult{
StationID: idAndWeight.ID.String(),
StationLocation: querier.GetLocation(idAndWeight.ID.String()),
Distance: idAndWeight.Distance,
//TODO codebear801 https://github.com/Telenav/osrm-backend/issues/321
Duration: idAndWeight.Distance,
}
results = append(results, tmp)
}

return querier.connectEndIntoGraph(stationID, results)
} else {
if querier.isStationConnectsToEnd(stationID) {
results := make([]*connectivitymap.QueryResult, 0, 1)
return querier.connectEndIntoGraph(stationID, results)
}
}

return nil
}

// GetLocation returns location of given station id
// Returns nil if given stationID is not found
func (querier *StationConnectivityQuerier) GetLocation(stationID string) *nav.Location {
switch stationID {
case stationfindertype.OrigLocationID:
return querier.startLocation
case stationfindertype.DestLocationID:
return querier.endLocation
default:
return querier.stationLocationQuerier.GetLocation(stationID)
}
}

func (querier *StationConnectivityQuerier) isStationConnectsToEnd(stationID string) bool {
_, ok := querier.reachableStationToEnd[stationID]
return ok
}

func (querier *StationConnectivityQuerier) connectEndIntoGraph(stationID string, results []*connectivitymap.QueryResult) []*connectivitymap.QueryResult {
if queryResult4End, ok := querier.reachableStationToEnd[stationID]; ok {
results = append(results, queryResult4End)
}

return results
}
Loading

0 comments on commit a4c42e7

Please sign in to comment.