Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/Build spatial index based on google::s2 #248

Merged
merged 8 commits into from
Mar 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions integration/cmd/chargestations-connectivity-gen/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// package main contains the tool of chargestation-connectivity generator
// stage 1:
// inputs is json file
// => convert to slice of [id:string,location: lat,lon]
// => calculate cellids for each point(for all levels)
// => build revese index for cellid -> ids
// stage 2:
// => iterate each point
// => generate a circle(s2::cap), find all cellids intersect with that circle
// => retrieve all ids
// => generate result of id(from), ids(all ids in certain distance)
// stage 3:
// => load data from file
// => for each line, its formid and all other ids
// => calculate distance between fromid and all other ids
// => sort result based on distance and write back to file
package main
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just put comments here, to be implemented.

10 changes: 10 additions & 0 deletions integration/cmd/chargestations-connectivity-gen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"fmt"
)

func main() {
// @todo: add logic to generate connectivity for charge stations
fmt.Print("Hello World!")
CodeBear801 marked this conversation as resolved.
Show resolved Hide resolved
}
1 change: 1 addition & 0 deletions integration/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/Telenav/osrm-backend/integration
go 1.13

require (
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/protobuf v1.3.2
github.com/golang/snappy v0.0.1
Expand Down
2 changes: 2 additions & 0 deletions integration/go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d h1:C/hKUcHT483btRbeGkrRjJz+Zbcj8audldIi9tRJDCc=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
Expand Down
20 changes: 20 additions & 0 deletions integration/service/spatialindexer/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// package spatialindexer answers query of nearest points(place, point of interest) for conditions
CodeBear801 marked this conversation as resolved.
Show resolved Hide resolved
// such as center location, radius, etc
//
// Sample Scenario 1: Build connectivity for charge stations during pro-processing
// indexer := NewS2Indexer().Build(poiCsvFile)
// for _, stationPoint := range chargeStations {
// nearbyStations := indexer.FindNearByIDs(stationPoint, 800km, -1)
// rankedStations := indexer.RankingIDsByShortestDistance(stationPoint, nearbyStations)
// }
//
//
// Sample Scenario 2: Dump S2Indexer's content to folder
// indexer.Dump(folderPath)
//
//
// Sample Scenario 3: Query reachable charge stations with current energy level
// indexer := NewS2Indexer().Load(folderPath)
// nearbyStations := indexer.FindNearByIDs(currentPoint, currentEnergyLevel, -1)
//
package spatialindexer
41 changes: 41 additions & 0 deletions integration/service/spatialindexer/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package spatialindexer

// Location for poi point
// @todo: will be replaced by the one in map
type Location struct {
Latitude float64
Longitude float64
}

// PointInfo records point related information such as ID and location
type PointInfo struct {
ID PointID
Location Location
}

// RankedPointInfo used to record ranking result, distance to specific point could be used for ranking
type RankedPointInfo struct {
PointInfo
Distance float64
}

// PointID defines ID for given point(location, point of interest)
// Only the data used for pre-processing contains valid PointID
type PointID int64

// Finder answers special query
type Finder interface {

// FindNearByPointIDs returns a group of points near to given center location
FindNearByPointIDs(center Location, radius float64, limitCount int) []PointInfo
}

// Ranker used to ranking a group of points
type Ranker interface {

// RankPointIDsByGreatCircleDistance ranks a group of points based on great circle distance to given location
RankPointIDsByGreatCircleDistance(center Location, nearByIDs []PointInfo) []RankedPointInfo

// RankPointIDsByShortestDistance ranks a group of points based on shortest path distance to given location
RankPointIDsByShortestDistance(center Location, nearByIDs []PointInfo) []RankedPointInfo
}
26 changes: 26 additions & 0 deletions integration/service/spatialindexer/poiloader/loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package poiloader

import (
"encoding/json"
"io/ioutil"

"github.com/golang/glog"
)

// LoadData accepts json file with points data and returns deserialized result
func LoadData(filePath string) ([]Element, error) {
var elements []Element

file, err := ioutil.ReadFile(filePath)
if err != nil {
glog.Errorf("While load file %s, met error %v\n", filePath, err)
return elements, err
}

err = json.Unmarshal(file, &elements)
if err != nil {
glog.Errorf("While unmarshal json file %s, met error %v\n", filePath, err)
return elements, err
}
return elements, nil
}
70 changes: 70 additions & 0 deletions integration/service/spatialindexer/poiloader/poi_format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package poiloader

// 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 *CategoryIDGather `json:"phone"`
EncodedPhone string `json:"encoded_phone"`
Email string `json:"email"`
WebURL string `json:"web_url"`
CategoryIDGather *CategoryIDGather `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
type Amenity string

const (
ChargingStation Amenity = "charging_station"
)

// Country records IOS code
type Country string

const (
Usa Country = "USA"
CodeBear801 marked this conversation as resolved.
Show resolved Hide resolved
)

// SpaceID is used to define a code for political defined administrative area
type SpaceID string

const (
UsaCA SpaceID = "USA_CA"
)

// VendorCode defines data source provider
type VendorCode string

const (
Noel VendorCode = "NOEL"
Noft VendorCode = "NOFT"
Nolp VendorCode = "NOLP"
)

// CategoryIDGather records aggregate data for certain type of point
type CategoryIDGather struct {
Integer *int64
String *string
}
64 changes: 64 additions & 0 deletions integration/service/spatialindexer/poiloader/sample_input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
[
{
"id":12345,
"vendor_code":"NOFT",
"vendor_poi_id":"NOFT-10_NREL99930@-@NOCO-37145190@-@NOCO-996595530",
"lat":0.48651,
"lon":0.66526,
"nav_lat":0.48651,
"nav_lon":0.66526,
"dis_lat":0.48651,
"dis_lon":0.66526,
"map_link_id":12345,
"side_of_street":"+",
"country":"USA",
"space_id":"USA_CA",
"airport_code":"NULL",
"is_national":false,
"is_state_importance":false,
"is_city_importance":false,
"fax":"NULL",
"phone":123456789,
"encoded_phone":"123456789",
"email":"NULL",
"web_url":"NULL",
"category_id_gather":"771``794``915",
"chain_gather":"1364@-@@-@",
"raw_category_gather":"NT@-@3578``NT@-@6000``NAICS@-@522110``NT@-@5540",
"child_gather":"NULL",
"parent_gather":"1718449@-@1``2216423@-@1",
"hilbert":2.32E+11,
"amenity":"charging_station"
},
{
"id":12346,
"vendor_code":"NOFT",
"vendor_poi_id":"NOFT-10_NREL99930@-@NOCO-37145190@-@NOCO-996595530",
"lat":0.48651,
"lon":0.66526,
"nav_lat":0.48651,
"nav_lon":0.66526,
"dis_lat":0.48651,
"dis_lon":0.66526,
"map_link_id":12345,
"side_of_street":"+",
"country":"USA",
"space_id":"USA_CA",
"airport_code":"NULL",
"is_national":false,
"is_state_importance":false,
"is_city_importance":false,
"fax":"NULL",
"phone":123456789,
"encoded_phone":"123456789",
"email":"NULL",
"web_url":"NULL",
"category_id_gather":"771``794``915",
"chain_gather":"1364@-@@-@",
"raw_category_gather":"NT@-@3578``NT@-@6000``NAICS@-@522110``NT@-@5540",
"child_gather":"NULL",
"parent_gather":"1718449@-@1``2216423@-@1",
"hilbert":2.32E+11,
"amenity":"charging_station"
}
]
51 changes: 51 additions & 0 deletions integration/service/spatialindexer/s2indexer/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package s2indexer

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

// https://s2geometry.io/resources/s2cell_statistics.html
// Level = 6 means average area size is 20754.64km2
// Level = 9 means average area size is 324.29km2
const minS2Level = 9

// Level = 19 means average area size is 309.27km2
// Level = 20 means average area size is 77.32km2
const maxS2Level = 20

func build(points []spatialindexer.PointInfo, minLevel, maxLevel int) map[s2.CellID][]spatialindexer.PointID {
pointID2CellIDs := make(map[spatialindexer.PointID][]s2.CellID)
cellID2PointIDs := make(map[s2.CellID][]spatialindexer.PointID)

for _, p := range points {
leafCellID := s2.CellFromLatLng(s2.LatLngFromDegrees(p.Location.Latitude, p.Location.Longitude)).ID()

var cellIDs []s2.CellID
// For level = 30, its parent equal to current
// So no need append leafCellID into cellIDs outside of for loop
for i := leafCellID.Level(); i > minLevel; i-- {
if i > maxLevel {
continue
}

parentCellID := leafCellID.Parent(i)
cellIDs = append(cellIDs, parentCellID)
}

pointID2CellIDs[p.ID] = cellIDs
}

for pointID, cellIDs := range pointID2CellIDs {
for _, cellID := range cellIDs {
if _, ok := cellID2PointIDs[cellID]; !ok {
var pointIDs []spatialindexer.PointID
cellID2PointIDs[cellID] = pointIDs
}

cellID2PointIDs[cellID] = append(cellID2PointIDs[cellID], pointID)
}
}

return cellID2PointIDs
}
Loading