Skip to content

Commit

Permalink
feat(internal): Using OSRM for calculating distance from backend
Browse files Browse the repository at this point in the history
Signed-off-by: Ashish Kumar <ashishkr23438@gmail.com>
  • Loading branch information
krashish8 committed Jan 27, 2022
1 parent 0b6375c commit 2a39915
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 2 deletions.
62 changes: 62 additions & 0 deletions internal/database/location.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*GRP-GNU-AGPL******************************************************************
File: location.go
Copyright (C) 2021 Team Georepublic <info@georepublic.de>
Developer(s):
Copyright (C) 2021 Ashish Kumar <ashishkr23438@gmail.com>
-----
This file is part of pg_scheduleserv.
pg_scheduleserv is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pg_scheduleserv is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with pg_scheduleserv. If not, see <https://www.gnu.org/licenses/>.
******************************************************************GRP-GNU-AGPL*/

package database

import (
"context"

"github.com/jackc/pgx/v4"
)

func (q *Queries) DBGetProjectLocations(ctx context.Context, project_id int64) ([]int64, error) {
sql := `
SELECT unnest(ARRAY[location_id]) AS location_id FROM jobs WHERE project_id = $1 UNION
SELECT unnest(ARRAY[p_location_id, d_location_id]) FROM shipments WHERE project_id = $1 UNION
SELECT unnest(ARRAY[start_id, end_id]) FROM vehicles WHERE project_id = $1`

rows, err := q.db.Query(ctx, sql, project_id)
if err != nil {
return nil, err
}
defer rows.Close()
return scanProjectLocationRows(rows)
}

func scanProjectLocationRows(rows pgx.Rows) ([]int64, error) {
var locations []int64
for rows.Next() {
var location_id int64
err := rows.Scan(&location_id)
if err != nil {
return nil, err
}
locations = append(locations, location_id)
}
return locations, nil
}
3 changes: 3 additions & 0 deletions internal/database/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ type Querier interface {
DBGetVehicle(ctx context.Context, id int64) (Vehicle, error)
DBUpdateVehicle(ctx context.Context, arg UpdateVehicleParams, vehicle_id int64) (Vehicle, error)
DBDeleteVehicle(ctx context.Context, id int64) (Vehicle, error)

// Locations
DBGetProjectLocations(ctx context.Context, project_id int64) ([]int64, error)
}

var _ Querier = (*Queries)(nil)
20 changes: 18 additions & 2 deletions internal/database/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,24 @@ import (
)

func (q *Queries) DBCreateSchedule(ctx context.Context, projectID int64) error {
query := fmt.Sprintf("SELECT create_schedule(%d)", projectID)
_, err := q.db.Exec(ctx, query)
// get project locations by calling DBGetProjectLocations
locationIds, err := q.DBGetProjectLocations(ctx, projectID)
if err != nil {
return err
}

startIds, endIds, durations, err := util.GetMatrix(locationIds)
if err != nil {
return err
}

// if either of startIds, endIds, durations is empty, return error
if len(startIds) == 0 || len(endIds) == 0 || len(durations) == 0 {
return fmt.Errorf("No locations present in the project")
}

query := "SELECT create_schedule_with_matrix($1, $2, $3, $4)"
_, err = q.db.Exec(ctx, query, projectID, startIds, endIds, durations)
return err
}

Expand Down
119 changes: 119 additions & 0 deletions internal/util/osrm_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*GRP-GNU-AGPL******************************************************************
File: osrm_api.go
Copyright (C) 2021 Team Georepublic <info@georepublic.de>
Developer(s):
Copyright (C) 2021 Ashish Kumar <ashishkr23438@gmail.com>
-----
This file is part of pg_scheduleserv.
pg_scheduleserv is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pg_scheduleserv is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with pg_scheduleserv. If not, see <https://www.gnu.org/licenses/>.
******************************************************************GRP-GNU-AGPL*/

package util

import (
"encoding/json"
"fmt"
"net/http"
"strings"
)

// make get request to an url with content-type, and return the response body as json
func Get(url string, contentType string, target interface{}) error {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
req.Header.Set("Content-Type", contentType)

res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
return fmt.Errorf("HTTP error: %s", res.Status)
}

return json.NewDecoder(res.Body).Decode(target)
}

func GetMatrix(locationIds []int64) (startIds []int64, endIds []int64, durations []int64, err error) {
// iterate through locationIds, convert all the ids to latitude and longitude, and append [longitude, latitude] in an array
coordinates := make([][]float64, 0)
for _, id := range locationIds {
latitude, longitude := GetCoordinates(id)
coordinates = append(coordinates, []float64{longitude, latitude})
}

// call the osrm api function to get the matrix
matrix, err := GetMatrixFromOSRM(coordinates)

if err != nil {
return nil, nil, nil, err
}

// iterate through the 2D matrix and append the start and end ids and durations. start id is locationIds[i], end id is locationIds[j], duration is matrix[i][j]
for i := 0; i < len(locationIds); i++ {
for j := 0; j < len(locationIds); j++ {
startIds = append(startIds, locationIds[i])
endIds = append(endIds, locationIds[j])
durations = append(durations, matrix[i][j])
}
}

return startIds, endIds, durations, nil
}

func GetMatrixFromOSRM(coordinates [][]float64) ([][]int64, error) {
// convert the coordinates to a string
coordinatesString := make([]string, 0)
for _, coordinate := range coordinates {
coordinatesString = append(coordinatesString, fmt.Sprintf("%.4f,%.4f", coordinate[0], coordinate[1]))
}

baseUrl := "http://router.project-osrm.org"

// call the osrm api function to get the matrix
url := fmt.Sprintf("%s/table/v1/driving/%s", baseUrl, strings.Join(coordinatesString, ";"))

// decode the response body as json, pass json in Get() function
response := make(map[string]interface{})
err := Get(url, "application/json", &response)
if err != nil {
return nil, err
}

// get the matrix from the response
matrix := response["durations"].([]interface{})

// convert the matrix to int64
matrixInt64 := make([][]int64, 0)
for _, row := range matrix {
rowInt64 := make([]int64, 0)
for _, value := range row.([]interface{}) {
rowInt64 = append(rowInt64, int64(value.(float64)))
}
matrixInt64 = append(matrixInt64, rowInt64)
}

return matrixInt64, nil
}

0 comments on commit 2a39915

Please sign in to comment.