From e23ecc6da52fad9995709a1b169189394c8395ee Mon Sep 17 00:00:00 2001 From: codebear801 Date: Tue, 12 May 2020 10:15:17 -0700 Subject: [PATCH] feat: integrate station connectivity finder with service issue: https://github.com/Telenav/osrm-backend/issues/242 --- .../cmd/place-connectivity-gen/main.go | 2 +- .../service/oasis/connectivitymap/builder.go | 5 ++ .../oasis/connectivitymap/connectivity_map.go | 4 +- .../oasis/connectivitymap/dumper_test.go | 2 +- integration/service/oasis/handler.go | 32 +++------ .../service/oasis/selectionstrategy/doc.go | 4 ++ .../has_enough_energy.go | 7 +- .../has_enough_energy_test.go | 10 +-- .../reachable_by_multiple_charge.go | 38 +++++++++-- .../reachable_by_multiple_charge_test.go | 22 +++--- .../reachable_by_single_charge.go | 15 ++-- .../reachable_by_single_charge_test.go | 2 +- .../selectionstrategy/resource_manager.go | 68 +++++++++++++++++++ .../spatialindexer/poiloader/poi_format.go | 60 ++++++++-------- .../stationfinder/station_finder_factory.go | 17 ++--- .../stations_iterator_alg_test.go | 2 +- 16 files changed, 188 insertions(+), 102 deletions(-) create mode 100644 integration/service/oasis/selectionstrategy/doc.go rename integration/service/oasis/{ => selectionstrategy}/has_enough_energy.go (87%) rename integration/service/oasis/{ => selectionstrategy}/has_enough_energy_test.go (81%) rename integration/service/oasis/{ => selectionstrategy}/reachable_by_multiple_charge.go (66%) rename integration/service/oasis/{ => selectionstrategy}/reachable_by_multiple_charge_test.go (95%) rename integration/service/oasis/{ => selectionstrategy}/reachable_by_single_charge.go (90%) rename integration/service/oasis/{ => selectionstrategy}/reachable_by_single_charge_test.go (92%) create mode 100644 integration/service/oasis/selectionstrategy/resource_manager.go diff --git a/integration/cmd/place-connectivity-gen/main.go b/integration/cmd/place-connectivity-gen/main.go index b7feac9ddab..2501d458ea5 100644 --- a/integration/cmd/place-connectivity-gen/main.go +++ b/integration/cmd/place-connectivity-gen/main.go @@ -37,7 +37,7 @@ func main() { } indexer.Dump(flags.outputFolder) - connectivitymap.NewConnectivityMap(flags.maxRange). + connectivitymap.New(flags.maxRange). Build(indexer, indexer, rankerStrategy, flags.numberOfWorkers). Dump(flags.outputFolder) diff --git a/integration/service/oasis/connectivitymap/builder.go b/integration/service/oasis/connectivitymap/builder.go index 867a01add49..8417160df7e 100644 --- a/integration/service/oasis/connectivitymap/builder.go +++ b/integration/service/oasis/connectivitymap/builder.go @@ -84,6 +84,11 @@ func (builder *connectivityMapBuilder) work(workerID int, source <-chan spatiali ids := make([]IDAndWeight, 0, len(rankedResults)) for _, r := range rankedResults { + // skip connectivity to itself + if r.ID == p.ID { + continue + } + ids = append(ids, IDAndWeight{ ID: r.ID, Weight: Weight{ diff --git a/integration/service/oasis/connectivitymap/connectivity_map.go b/integration/service/oasis/connectivitymap/connectivity_map.go index 9c547cf38b0..13e3a90852d 100644 --- a/integration/service/oasis/connectivitymap/connectivity_map.go +++ b/integration/service/oasis/connectivitymap/connectivity_map.go @@ -27,8 +27,8 @@ type ConnectivityMap struct { statistic *statistic } -// NewConnectivityMap creates ConnectivityMap -func NewConnectivityMap(maxRange float64) *ConnectivityMap { +// New creates ConnectivityMap +func New(maxRange float64) *ConnectivityMap { return &ConnectivityMap{ maxRange: maxRange, statistic: newStatistic(), diff --git a/integration/service/oasis/connectivitymap/dumper_test.go b/integration/service/oasis/connectivitymap/dumper_test.go index 48929974fc0..514d3a6e7ad 100644 --- a/integration/service/oasis/connectivitymap/dumper_test.go +++ b/integration/service/oasis/connectivitymap/dumper_test.go @@ -36,7 +36,7 @@ func TestDumpGivenObjectThenLoadAndThenCompareWithOriginalObject(t *testing.T) { t.Errorf("During running serializeConnectivityMap for case %v, met error %v", c, err) } - actual := NewConnectivityMap(0.0) + actual := New(0.0) if err := deSerializeConnectivityMap(actual, path); err != nil { t.Errorf("During running deSerializeConnectivityMap for case %v, met error %v", c, err) } diff --git a/integration/service/oasis/handler.go b/integration/service/oasis/handler.go index 9752d9abf71..062a4de6632 100644 --- a/integration/service/oasis/handler.go +++ b/integration/service/oasis/handler.go @@ -6,36 +6,26 @@ import ( "net/http" "github.com/Telenav/osrm-backend/integration/api/oasis" - "github.com/Telenav/osrm-backend/integration/service/oasis/osrmconnector" "github.com/Telenav/osrm-backend/integration/service/oasis/osrmhelper" - "github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder" + "github.com/Telenav/osrm-backend/integration/service/oasis/selectionstrategy" "github.com/golang/glog" ) // Handler handles oasis request and provide response type Handler struct { - osrmConnector *osrmconnector.OSRMConnector - finder stationfinder.StationFinder + resourceMgr *selectionstrategy.ResourceMgr } // New creates new Handler object func New(osrmBackend, finderType, searchEndpoint, apiKey, apiSignature, dataFolderPath string) (*Handler, error) { - // @todo: need make sure connectivity is on and continues available - // simple request to guarantee server is alive after init - if len(osrmBackend) == 0 { - err := fmt.Errorf("empty osrmBackend end point") - return nil, err - } - - finder, err := stationfinder.CreateStationsFinder(finderType, searchEndpoint, apiKey, apiSignature, dataFolderPath) + resourceMgr, err := selectionstrategy.NewResourceMgr(osrmBackend, finderType, searchEndpoint, apiKey, apiSignature, dataFolderPath) if err != nil { - glog.Errorf("Failed in Handler's New() when try to call CreateStationsFinder(), met error = %+v\n", err) + glog.Errorf("Failed to create Handler due to error %+v.\n", err) return nil, err } return &Handler{ - osrmConnector: osrmconnector.NewOSRMConnector(osrmBackend), - finder: finder, + resourceMgr: resourceMgr, }, nil } @@ -52,7 +42,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // generate route response based on given oasis's orig/destination - routeResp, err := osrmhelper.RequestRoute4InputOrigDest(oasisReq, h.osrmConnector) + routeResp, err := osrmhelper.RequestRoute4InputOrigDest(oasisReq, h.resourceMgr.OSRMConnector()) if err != nil { glog.Error(err) w.WriteHeader(http.StatusOK) @@ -70,7 +60,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // check whether has enough energy - b, remainRange, err := hasEnoughEnergy(oasisReq.CurrRange, oasisReq.SafeLevel, routeResp) + b, remainRange, err := selectionstrategy.HasEnoughEnergy(oasisReq.CurrRange, oasisReq.SafeLevel, routeResp) if err != nil { glog.Error(err) w.WriteHeader(http.StatusOK) @@ -78,19 +68,19 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } if b { - generateOASISResponse4NoChargeNeeded(w, routeResp, remainRange) + selectionstrategy.GenerateOASISResponse4NoChargeNeeded(w, routeResp, remainRange) return } // check whether could achieve by single charge - overlap := getOverlapChargeStations4OrigDest(oasisReq, routeResp.Routes[0].Distance, h.osrmConnector, h.finder) + overlap := selectionstrategy.GetOverlapChargeStations4OrigDest(oasisReq, routeResp.Routes[0].Distance, h.resourceMgr) if len(overlap) > 0 { - generateResponse4SingleChargeStation(w, oasisReq, overlap, h.osrmConnector) + selectionstrategy.GenerateResponse4SingleChargeStation(w, oasisReq, overlap, h.resourceMgr) return } // generate result for multiple charge - generateSolutions4MultipleCharge(w, oasisReq, routeResp, h.osrmConnector, h.finder) + selectionstrategy.GenerateSolutions4MultipleCharge(w, oasisReq, routeResp, h.resourceMgr) return } diff --git a/integration/service/oasis/selectionstrategy/doc.go b/integration/service/oasis/selectionstrategy/doc.go new file mode 100644 index 00000000000..3a9e447065f --- /dev/null +++ b/integration/service/oasis/selectionstrategy/doc.go @@ -0,0 +1,4 @@ +/* +Package selectionstrategy contains logic related with charge station selection related logic. +*/ +package selectionstrategy diff --git a/integration/service/oasis/has_enough_energy.go b/integration/service/oasis/selectionstrategy/has_enough_energy.go similarity index 87% rename from integration/service/oasis/has_enough_energy.go rename to integration/service/oasis/selectionstrategy/has_enough_energy.go index bc4d8a68294..4885f2a5fa6 100644 --- a/integration/service/oasis/has_enough_energy.go +++ b/integration/service/oasis/selectionstrategy/has_enough_energy.go @@ -1,4 +1,4 @@ -package oasis +package selectionstrategy import ( "encoding/json" @@ -13,7 +13,7 @@ import ( // currRange: current energy level, represent by distance(unit: meters) // destRange: energy level required to destination // currRange - routeDistance > destRange means no charge station is needed -func hasEnoughEnergy(currRange, destRange float64, routeResp *route.Response) (bool, float64, error) { +func HasEnoughEnergy(currRange, destRange float64, routeResp *route.Response) (bool, float64, error) { if len(routeResp.Routes) == 0 { err := fmt.Errorf("route response contains no route result") return false, 0, err @@ -42,7 +42,8 @@ func hasEnoughEnergy(currRange, destRange float64, routeResp *route.Response) (b return false, remainRange, nil } -func generateOASISResponse4NoChargeNeeded(w http.ResponseWriter, routeResp *route.Response, remainRange float64) { +// GenerateOASISResponse4NoChargeNeeded generates response for no charge needed +func GenerateOASISResponse4NoChargeNeeded(w http.ResponseWriter, routeResp *route.Response, remainRange float64) { w.WriteHeader(http.StatusOK) solution := new(oasis.Solution) diff --git a/integration/service/oasis/has_enough_energy_test.go b/integration/service/oasis/selectionstrategy/has_enough_energy_test.go similarity index 81% rename from integration/service/oasis/has_enough_energy_test.go rename to integration/service/oasis/selectionstrategy/has_enough_energy_test.go index af25f6433f9..24fc19e17d7 100644 --- a/integration/service/oasis/has_enough_energy_test.go +++ b/integration/service/oasis/selectionstrategy/has_enough_energy_test.go @@ -1,4 +1,4 @@ -package oasis +package selectionstrategy import ( "strconv" @@ -10,12 +10,12 @@ import ( func TestHasEnoughEnergyPositive1(t *testing.T) { response := &route.Response{ - Routes: []*route.Route{&route.Route{Distance: 10000.0}}, + Routes: []*route.Route{{Distance: 10000.0}}, } currRange := 20000.0 destRange := 5000.0 - b, remainRange, err := hasEnoughEnergy(currRange, destRange, response) + b, remainRange, err := HasEnoughEnergy(currRange, destRange, response) if !b || err != nil { t.Errorf("Incorrect result generated for TesthasEnoughEnergyPositive1, return value is (%t, %v)", b, err) } @@ -29,12 +29,12 @@ func TestHasEnoughEnergyPositive1(t *testing.T) { func TestHasEnoughEnergyPositive2(t *testing.T) { response := &route.Response{ - Routes: []*route.Route{&route.Route{Distance: 10000.0}}, + Routes: []*route.Route{{Distance: 10000.0}}, } currRange := 10000.0 destRange := 5000.0 - b, remainRange, err := hasEnoughEnergy(currRange, destRange, response) + b, remainRange, err := HasEnoughEnergy(currRange, destRange, response) if b || err != nil { t.Errorf("Incorrect result generated for TesthasEnoughEnergyPositive1, return value is (%t, %v)", b, err) } diff --git a/integration/service/oasis/reachable_by_multiple_charge.go b/integration/service/oasis/selectionstrategy/reachable_by_multiple_charge.go similarity index 66% rename from integration/service/oasis/reachable_by_multiple_charge.go rename to integration/service/oasis/selectionstrategy/reachable_by_multiple_charge.go index 0dff22a40fd..12a84834def 100644 --- a/integration/service/oasis/reachable_by_multiple_charge.go +++ b/integration/service/oasis/selectionstrategy/reachable_by_multiple_charge.go @@ -1,4 +1,4 @@ -package oasis +package selectionstrategy import ( "encoding/json" @@ -10,6 +10,8 @@ import ( "github.com/Telenav/osrm-backend/integration/service/oasis/chargingstrategy" "github.com/Telenav/osrm-backend/integration/service/oasis/haversine" "github.com/Telenav/osrm-backend/integration/service/oasis/osrmconnector" + "github.com/Telenav/osrm-backend/integration/service/oasis/spatialindexer/ranker" + "github.com/Telenav/osrm-backend/integration/service/oasis/stationconnquerier" "github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder" "github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder/stationfinderalg" "github.com/Telenav/osrm-backend/integration/service/oasis/stationgraph" @@ -18,21 +20,43 @@ import ( "github.com/twpayne/go-polyline" ) +// GenerateSolutions4MultipleCharge generates solutions for multiple charge stations needed from orig to dest based on current energy status // @todo: handle negative situation -func generateSolutions4MultipleCharge(w http.ResponseWriter, oasisReq *oasis.Request, routeResp *route.Response, oc *osrmconnector.OSRMConnector, finder stationfinder.StationFinder) { - solutions := generateSolutionsWithEarlistArrival(oasisReq, routeResp, oc, finder) +func GenerateSolutions4MultipleCharge(w http.ResponseWriter, oasisReq *oasis.Request, routeResp *route.Response, resourceMgr *ResourceMgr) { + //solutions := generateSolutions4SearchAlongRoute(oasisReq, routeResp, resourceMgr.osrmConnector, resourceMgr.stationFinder) + solutions := generateSolutions4ChargeStationBasedRoute(oasisReq, resourceMgr) w.WriteHeader(http.StatusOK) r := new(oasis.Response) r.Code = "200" r.Message = "Success." - for _, sol := range solutions { - r.Solutions = append(r.Solutions, sol) - } + r.Solutions = append(r.Solutions, solutions...) json.NewEncoder(w).Encode(r) } -func generateSolutionsWithEarlistArrival(oasisReq *oasis.Request, routeResp *route.Response, oc *osrmconnector.OSRMConnector, finder stationfinder.StationFinder) []*oasis.Solution { +func generateSolutions4ChargeStationBasedRoute(oasisReq *oasis.Request, resourceMgr *ResourceMgr) []*oasis.Solution { + targetSolutions := make([]*oasis.Solution, 0, 10) + querier := stationconnquerier.New(resourceMgr.spatialIndexerFinder, + ranker.CreateRanker(ranker.SimpleRanker, resourceMgr.osrmConnector), + resourceMgr.stationLocationQuerier, + resourceMgr.connectivityMap, + &nav.Location{Lat: oasisReq.Coordinates[0].Lat, Lon: oasisReq.Coordinates[0].Lon}, + &nav.Location{Lat: oasisReq.Coordinates[1].Lat, Lon: oasisReq.Coordinates[1].Lon}, + oasisReq.CurrRange, + oasisReq.MaxRange) + internalSolutions := stationgraph.NewStationGraph(oasisReq.CurrRange, oasisReq.MaxRange, + chargingstrategy.NewFakeChargingStrategy(oasisReq.MaxRange), + querier).GenerateChargeSolutions() + + for _, sol := range internalSolutions { + targetSolution := sol.Convert2ExternalSolution() + targetSolutions = append(targetSolutions, targetSolution) + } + + return targetSolutions +} + +func generateSolutions4SearchAlongRoute(oasisReq *oasis.Request, routeResp *route.Response, oc *osrmconnector.OSRMConnector, finder stationfinder.StationFinder) []*oasis.Solution { targetSolutions := make([]*oasis.Solution, 0) chargeLocations := chargeLocationSelection(oasisReq, routeResp) diff --git a/integration/service/oasis/reachable_by_multiple_charge_test.go b/integration/service/oasis/selectionstrategy/reachable_by_multiple_charge_test.go similarity index 95% rename from integration/service/oasis/reachable_by_multiple_charge_test.go rename to integration/service/oasis/selectionstrategy/reachable_by_multiple_charge_test.go index cfbe581f2a6..32fa6c1569f 100644 --- a/integration/service/oasis/reachable_by_multiple_charge_test.go +++ b/integration/service/oasis/selectionstrategy/reachable_by_multiple_charge_test.go @@ -1,4 +1,4 @@ -package oasis +package selectionstrategy import ( "testing" @@ -9,42 +9,42 @@ import ( var fakeRoute1 route.Route = route.Route{ Legs: []*route.Leg{ - &route.Leg{ + { Distance: 37386.2, Steps: []*route.Step{ - &route.Step{ + { Distance: 449.5, Geometry: "y~ecF|_qgVm@@kAJw@RqAf@a@PmBzAqA|Ae@v@a@|@aAzC", }, - &route.Step{ + { Distance: 243, Geometry: "irfcFdqqgVEp@Ib@MZe@x@k@j@c@R_@Fm@DiBA", }, - &route.Step{ + { Distance: 207.7, Geometry: "u|fcF|xqgV?zA?xA?h@?j@@tA?hA?h@", }, - &route.Step{ + { Distance: 344.6, Geometry: "s|fcFrgrgV@h@tKHdA@t@@v@@", }, - &route.Step{ + { Distance: 28988.5, Geometry: "gjfcFlirgVb@PZHZHj@PRJTJVRRTNXNd@DZ@\\A\\C`@Ih@Ih@Mn@Ot@OfA_@dDShAgA|FQ~@e@bCa@xBq@pDqB|K{@zEw@|Eq@vEO~@ANKr@EZGh@G`@[|Bm@hE_@fCIn@E^EZYpBw@bGM|@_AdHgBzN_@rCa@`CETCPIn@yAxKq@bFy@hGIh@e@fDKx@UbBCRId@Id@G`@}@dHy@tGKp@Kv@UdBIl@k@bEgAnIIt@Ih@Ip@If@YjBa@tCGf@Il@q@zEKv@g@rDuAfKg@xDsA~JyAxKOlAIp@QrAObAOjAKt@Kz@_@tCm@bEYpBUpA_@xBg@xCWtAa@|BYxAIb@AJMx@UtAa@~BUdBQjA_AdHqBhOYvBq@dFc@hDwAdKMz@Ir@Ib@Kt@QvAKp@]xBS~@g@nBIVIZYdAMb@q@bCK^k@jBcA`DcAbDaA`D{@jCQl@KXy@rC_A`Dc@|AOh@g@`Bw@fCK^cAxDSr@cAnDQj@Sn@w@dCu@jBGNiAzB[j@[j@aAdBINGJgAdBc@p@s@bAiAdBeAbBuB`DMPkDpFsCjEe@t@aCpD_@l@Yb@aBdCoAnBOR_AnASVOTGHMPu@jAiAdBm@`A_FzH_CnDqAlBaAvAy@bA_AjAGF_CtCcFlFeBjBkCrC[ZUVu@v@_@^mArAg@h@oCvC_@`@{C~CoArAqArASTqBtBgAjA}@`A{A~Ag@h@YZq@r@gCjCwB|BkDtDcBfBuHdIyA|AkAnAOPsAtAqAtA]^WXWX}@`Ae@f@QPcGnGoApAoCxCeHtHeJrJg@h@qAvA{FdGONMLoDvDs@v@oCzCg@l@w@~@cArAeAzA[f@]l@QXcB|Ci@hAs@zAm@lAs@xAs@xAaBjDeChFiD`HaDjGUb@qBhEsAtC_@t@iBpD[n@wArCwAtCwCjGS`@oCpFoCrFgAxBc@|@cAlBu@vA}@pBs@xAuEjJyAxCgG`MmBzDaAnBc@z@q@rAo@rAsAjCy@`BKXITIPkA|BkAbCeBjD_AjBe@~@y@dBk@fAmBzDqEdJcKpSWh@Sb@kA~BINGLs@vAq@tAwDvHeBlD{@dBmBzDe@`AKRo@tA_@v@Yh@cAnBcApBi@fA_CvEyA|Cm@pAuBlEm@tAu@lBo@|AkAlDABOh@CJCJCHGPADITSr@CJGTENe@dBi@zBw@|D[hBG\\[bB{@dFO~@G`@c@zDUhBQdBO`B]bEC^OxBcAzOiBbZIjAc@bH}@lN{@~MMtBw@bMUxCKpAY~Ck@|EGh@a@xCOfAIf@Id@Ib@Mx@Kn@Q`AG\\G\\Mh@I^Qv@Ml@i@tBW`AYfAQn@i@fBm@pBk@nB_@hAk@fBOd@eArCwBlGo@hBOb@Qf@}@jC_AlCs@tBs@~Bs@zBe@bBUx@U`AW`AU`AUdAU`A[vA[xA]hBId@Id@I`@UdAIl@]jBqC~Ne@bCKh@Kj@Q~@Mj@Kj@a@nBYtAMr@Mh@Oj@e@~Aw@`Cg@pAo@vAgAvBgAlBg@p@s@~@cBrBeE~EmAvAcAjAsA|AwChDiBvB{AhBuFvG{CpDaAhAcF`GeJrKyJhLu@|@ePnR]`@WZoAvAsEpFyFxGg@l@}@dAoAvA_BlByAdBqAzAcE|EoAzAcAjAqEjFyEtF{@bAe@h@", }, - &route.Step{ + { Distance: 467.2, Geometry: "mx_dFbfgiVcAv@s@j@WV[\\eA`Am@h@]V[R[JYF_@D]@yAG[?W@UDUFSHSLQLc@`@", }, - &route.Step{ + { Distance: 6035.5, Geometry: "so`dF`sgiVGf@NVR\\RZv@jAZf@LTV^RXl@~@b@r@`@n@TXRV~@dA^^d@b@VRFFTPf@\\BB^VXN`@TRJ\\R`Ah@`@TRJ`@NVLTLd@b@\\`@HJVXNJVLLPNPX^X^RVTVRVTVLPNPNPLPX\\RRLNNRZ^XZd@n@LPZ^DFBDRVr@z@RPZRJDPHXHj@JnATF@h@Jd@PPHd@ZPRPVR^LXNl@@HHr@Bb@Bb@@^B\\JbCBb@D`ADl@Dp@Dn@@XDf@DTHTLZV^JNPRVR\\VFDf@ZLHXNVXb@|@Rx@@DLp@Ln@ZbBXxADPRfAb@zBNv@Jj@FZR~@RpA`@bCLx@Nx@FVP~@^rBZ|AJh@DRf@fCJf@BNJd@RdA?P?DDRDVF`@Jf@FZJf@FZFZLn@FVFXX~AHb@Hb@Pz@Pz@Jd@PbARbALz@BNFd@B`@@f@@z@?~B?R?\\?x@?p@?lC?Z@l@Dz@Dx@?l@A|@Et@Kv@Kh@Md@Ob@MXEHMTQZ]n@KRKROZEHABQf@I\\GVETCNG^En@A`@Ad@@b@Dx@D|@@\\@b@@^@^?j@@dA?\\@f@Ax@A^Cj@Iz@APE`@E`@Gj@Gh@I`AEj@Cj@AnABdAFfAFz@Fl@Fp@BRH|@L~AFl@BVFl@BXDb@HbA^`FHjAz@hKDh@Fr@Ht@Hv@P~AFv@Hx@HfADj@B`@Dr@F|@Bl@Bj@@f@@`A?bAAx@Ez@IhAIx@UfBKx@Gr@UxCUxCCXEl@Eb@C^E`@C`@Gl@KdAGn@", }, - &route.Step{ + { Distance: 650.3, Geometry: "aq}cFfesiVK^OXQLQDy@@O?OBYLeAr@[N_@Fa@DUAu@IcAOSCe@Em@Ei@?mA@u@Bo@Dg@Jk@Vw@PcAZcA`@KF", }, - &route.Step{ + { Distance: 0, Geometry: "os~cFvmsiV", }, diff --git a/integration/service/oasis/reachable_by_single_charge.go b/integration/service/oasis/selectionstrategy/reachable_by_single_charge.go similarity index 90% rename from integration/service/oasis/reachable_by_single_charge.go rename to integration/service/oasis/selectionstrategy/reachable_by_single_charge.go index d2fba25c2aa..3fa9ec08b74 100644 --- a/integration/service/oasis/reachable_by_single_charge.go +++ b/integration/service/oasis/selectionstrategy/reachable_by_single_charge.go @@ -1,4 +1,4 @@ -package oasis +package selectionstrategy import ( "encoding/json" @@ -12,7 +12,6 @@ import ( "github.com/Telenav/osrm-backend/integration/api/search/nearbychargestation" "github.com/Telenav/osrm-backend/integration/service/oasis/osrmconnector" "github.com/Telenav/osrm-backend/integration/service/oasis/osrmhelper" - "github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder" "github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder/stationfinderalg" "github.com/golang/glog" ) @@ -25,14 +24,14 @@ const maxOverlapPointsNum = 500 // The energy level is safeRange + nearest charge station's distance to destination // If there is one or several charge stations could be found in both origStationsResp and destStationsResp // We think the result is reachable by single charge station -func getOverlapChargeStations4OrigDest(req *oasis.Request, routedistance float64, osrmConnector *osrmconnector.OSRMConnector, finder stationfinder.StationFinder) osrm.Coordinates { +func GetOverlapChargeStations4OrigDest(req *oasis.Request, routedistance float64, resourceMgr *ResourceMgr) osrm.Coordinates { // only possible when currRange + maxRange > distance + safeRange if req.CurrRange+req.MaxRange < routedistance+req.SafeLevel { return nil } - origStations := finder.NewOrigStationFinder(req) - destStations := finder.NewDestStationFinder(req) + origStations := resourceMgr.stationFinder.NewOrigStationFinder(req) + destStations := resourceMgr.stationFinder.NewDestStationFinder(req) overlap := stationfinderalg.FindOverlapBetweenStations(origStations, destStations) if len(overlap) == 0 { @@ -61,9 +60,9 @@ type singleChargeStationCandidate struct { durationToDest float64 } -// @todo these logic might refactored later: charge station status calculation should be moved away -func generateResponse4SingleChargeStation(w http.ResponseWriter, req *oasis.Request, overlapPoints osrm.Coordinates, osrmConnector *osrmconnector.OSRMConnector) bool { - candidate, err := pickChargeStationWithEarlistArrival(req, overlapPoints, osrmConnector) +// GenerateResponse4SingleChargeStation generates response for only single charge station needed from orig to dest based on current energy status +func GenerateResponse4SingleChargeStation(w http.ResponseWriter, req *oasis.Request, overlapPoints osrm.Coordinates, resourceMgr *ResourceMgr) bool { + candidate, err := pickChargeStationWithEarlistArrival(req, overlapPoints, resourceMgr.osrmConnector) if err != nil { w.WriteHeader(http.StatusOK) diff --git a/integration/service/oasis/reachable_by_single_charge_test.go b/integration/service/oasis/selectionstrategy/reachable_by_single_charge_test.go similarity index 92% rename from integration/service/oasis/reachable_by_single_charge_test.go rename to integration/service/oasis/selectionstrategy/reachable_by_single_charge_test.go index f43418a13d5..1326248b38b 100644 --- a/integration/service/oasis/reachable_by_single_charge_test.go +++ b/integration/service/oasis/selectionstrategy/reachable_by_single_charge_test.go @@ -1,4 +1,4 @@ -package oasis +package selectionstrategy import ( "testing" diff --git a/integration/service/oasis/selectionstrategy/resource_manager.go b/integration/service/oasis/selectionstrategy/resource_manager.go new file mode 100644 index 00000000000..c74158adbbe --- /dev/null +++ b/integration/service/oasis/selectionstrategy/resource_manager.go @@ -0,0 +1,68 @@ +package selectionstrategy + +import ( + "fmt" + + "github.com/Telenav/osrm-backend/integration/service/oasis/connectivitymap" + "github.com/Telenav/osrm-backend/integration/service/oasis/osrmconnector" + "github.com/Telenav/osrm-backend/integration/service/oasis/spatialindexer" + "github.com/Telenav/osrm-backend/integration/service/oasis/spatialindexer/s2indexer" + "github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder" + "github.com/golang/glog" +) + +type StaionSelectionStrategy int + +const ( + FindChargeStaionsAlongRoute = StaionSelectionStrategy(iota) + 1 + ChargeStaionBasedRouting +) + +// ResourceMgr defines strategy be used by charge station selection +type ResourceMgr struct { + osrmConnector *osrmconnector.OSRMConnector // osrmConnector represents communication with OSRM backend + stationFinder stationfinder.StationFinder // stationFinder generates nearby stations based cloud search or local spatial index + spatialIndexerFinder spatialindexer.Finder // spatialIndexerFinder answers spatial query based on pre-generated spatial data + connectivityMap *connectivitymap.ConnectivityMap // connectivityMap contains connectivity information for stations + stationLocationQuerier spatialindexer.PlaceLocationQuerier // stationLocationQuerier answers location information for specific station +} + +// NewResourceMgr creates ResourceMgr object +func NewResourceMgr(osrmBackend, finderType, searchEndpoint, apiKey, apiSignature, dataFolderPath string) (*ResourceMgr, error) { + // @todo: need make sure connectivity is on and continues available + // simple request to guarantee server is alive after init + if len(osrmBackend) == 0 { + err := fmt.Errorf("empty osrmBackend end point") + return nil, err + } + + s2indexer := s2indexer.NewS2Indexer().Load(dataFolderPath) + if s2indexer == nil { + err := fmt.Errorf("failed to load s2Indexer") + return nil, err + } + + stationFinder, err := stationfinder.CreateStationsFinder(finderType, searchEndpoint, apiKey, apiSignature, s2indexer) + if err != nil { + glog.Errorf("Failed to call stationfinder.CreateStationsFinder, met error = %+v\n", err) + return nil, err + } + + connectivityMap := connectivitymap.New(0.0).Load(dataFolderPath) + if connectivityMap == nil { + err := fmt.Errorf("failed to load ConnectivityMap") + return nil, err + } + + return &ResourceMgr{ + osrmConnector: osrmconnector.NewOSRMConnector(osrmBackend), + stationFinder: stationFinder, + spatialIndexerFinder: s2indexer, + connectivityMap: connectivityMap, + stationLocationQuerier: s2indexer, + }, nil +} + +func (r *ResourceMgr) OSRMConnector() *osrmconnector.OSRMConnector { + return r.osrmConnector +} diff --git a/integration/service/oasis/spatialindexer/poiloader/poi_format.go b/integration/service/oasis/spatialindexer/poiloader/poi_format.go index 23d6b56e3ca..14ca58d1371 100644 --- a/integration/service/oasis/spatialindexer/poiloader/poi_format.go +++ b/integration/service/oasis/spatialindexer/poiloader/poi_format.go @@ -1,38 +1,36 @@ package poiloader -import "encoding/json" - // Element represent information loaded for point record type Element struct { - ID int64 `json:"id"` - VendorCode VendorCode `json:"vendor_code"` - VendorPoiID string `json:"vendor_poi_id"` - Lat float64 `json:"lat"` - Lon float64 `json:"lon"` - NavLat float64 `json:"nav_lat"` - NavLon float64 `json:"nav_lon"` - DisLat float64 `json:"dis_lat"` - DisLon float64 `json:"dis_lon"` - MapLinkID int64 `json:"map_link_id"` - SideOfStreet string `json:"side_of_street"` - Country Country `json:"country"` - SpaceID SpaceID `json:"space_id"` - AirportCode string `json:"airport_code"` - IsNational bool `json:"is_national"` - IsStateImportance bool `json:"is_state_importance"` - IsCityImportance bool `json:"is_city_importance"` - Fax string `json:"fax"` - Phone json.Number `json:"phone"` - EncodedPhone string `json:"encoded_phone"` - Email string `json:"email"` - WebURL string `json:"web_url"` - CategoryIDGather json.Number `json:"category_id_gather"` - ChainGather string `json:"chain_gather"` - RawCategoryGather string `json:"raw_category_gather"` - ChildGather string `json:"child_gather"` - ParentGather string `json:"parent_gather"` - Hilbert float64 `json:"hilbert"` - Amenity Amenity `json:"amenity"` + ID int64 `json:"id"` + VendorCode VendorCode `json:"vendor_code"` + VendorPoiID string `json:"vendor_poi_id"` + Lat float64 `json:"lat"` + Lon float64 `json:"lon"` + NavLat float64 `json:"nav_lat"` + NavLon float64 `json:"nav_lon"` + DisLat float64 `json:"dis_lat"` + DisLon float64 `json:"dis_lon"` + MapLinkID int64 `json:"map_link_id"` + SideOfStreet string `json:"side_of_street"` + Country Country `json:"country"` + SpaceID SpaceID `json:"space_id"` + AirportCode string `json:"airport_code"` + IsNational bool `json:"is_national"` + IsStateImportance bool `json:"is_state_importance"` + IsCityImportance bool `json:"is_city_importance"` + Fax string `json:"fax"` + //Phone json.Number `json:"phone"` + EncodedPhone string `json:"encoded_phone"` + Email string `json:"email"` + WebURL string `json:"web_url"` + //CategoryIDGather json.Number `json:"category_id_gather"` + ChainGather string `json:"chain_gather"` + RawCategoryGather string `json:"raw_category_gather"` + ChildGather string `json:"child_gather"` + ParentGather string `json:"parent_gather"` + Hilbert float64 `json:"hilbert"` + Amenity Amenity `json:"amenity"` } // Amenity is defined by OSM format: https://wiki.openstreetmap.org/wiki/Tag:amenity%3Dcharging_station diff --git a/integration/service/oasis/stationfinder/station_finder_factory.go b/integration/service/oasis/stationfinder/station_finder_factory.go index f66e075d0a9..2349b1aa95f 100644 --- a/integration/service/oasis/stationfinder/station_finder_factory.go +++ b/integration/service/oasis/stationfinder/station_finder_factory.go @@ -4,8 +4,9 @@ import ( "fmt" "github.com/Telenav/osrm-backend/integration/service/oasis/searchconnector" - "github.com/Telenav/osrm-backend/integration/service/oasis/spatialindexer/s2indexer" + "github.com/Telenav/osrm-backend/integration/service/oasis/spatialindexer" "github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder/cloudfinder" + "github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder/localfinder" "github.com/golang/glog" ) @@ -18,8 +19,8 @@ const ( ) // CreateStationsFinder creates finder which implements StationFinder interface -func CreateStationsFinder(finderType, searchEndpoint, apiKey, apiSignature, dataFolderPath string) (StationFinder, error) { - if err := checkInput(finderType, searchEndpoint, apiKey, apiSignature, dataFolderPath); err != nil { +func CreateStationsFinder(finderType, searchEndpoint, apiKey, apiSignature string, finder spatialindexer.Finder) (StationFinder, error) { + if err := checkInput(finderType, searchEndpoint, apiKey, apiSignature, finder); err != nil { return nil, err } @@ -30,11 +31,7 @@ func CreateStationsFinder(finderType, searchEndpoint, apiKey, apiSignature, data return cloudfinder.New(searchFinder), nil case LocalFinder: - localIndex := s2indexer.NewS2Indexer().Load(dataFolderPath) - if localIndex == nil { - err := fmt.Errorf("failed to load s2Indexer") - return nil, err - } + return localfinder.New(finder), nil } return nil, nil @@ -45,7 +42,7 @@ func isValidStationFinderType(finderType string) bool { return finderType == CloudFinder || finderType == LocalFinder } -func checkInput(finderType, searchEndpoint, apiKey, apiSignature, dataFolderPath string) error { +func checkInput(finderType, searchEndpoint, apiKey, apiSignature string, finder spatialindexer.Finder) error { if !isValidStationFinderType(finderType) { glog.Error("Try to create finder not implemented yet, can only choose CloudFinder or LocalFinder for now.\n") err := fmt.Errorf("invalid station finder type") @@ -61,7 +58,7 @@ func checkInput(finderType, searchEndpoint, apiKey, apiSignature, dataFolderPath } if finderType == LocalFinder && - len(dataFolderPath) == 0 { + finder == nil { err := fmt.Errorf("empty input for local index") return err } diff --git a/integration/service/oasis/stationfinder/stationfinderalg/stations_iterator_alg_test.go b/integration/service/oasis/stationfinder/stationfinderalg/stations_iterator_alg_test.go index 71f1e674a06..5b18c4892b2 100644 --- a/integration/service/oasis/stationfinder/stationfinderalg/stations_iterator_alg_test.go +++ b/integration/service/oasis/stationfinder/stationfinderalg/stations_iterator_alg_test.go @@ -281,7 +281,7 @@ func TestCalculateWeightBetweenNeighbors(t *testing.T) { oc := osrmconnector.NewOSRMConnector(ts.URL) // create finder based on fake TNSearchService - finder, err := stationfinder.CreateStationsFinder(stationfinder.CloudFinder, ts.URL, "apikey", "apisignature", "") + finder, err := stationfinder.CreateStationsFinder(stationfinder.CloudFinder, ts.URL, "apikey", "apisignature", nil) if err != nil { t.Errorf("Failed to create station finder during TestCalculateWeightBetweenNeighbors with error = %+v.\n", err) }