Skip to content

Commit

Permalink
Merge pull request #12 from SmartWasteSegregatorAndRoutePlanner/imple…
Browse files Browse the repository at this point in the history
…ment-djikstras-algorithm

Implement djikstras algorithm
  • Loading branch information
dmdhrumilmistry committed Feb 3, 2023
2 parents 91c20d7 + af9ac7a commit 45349c1
Show file tree
Hide file tree
Showing 12 changed files with 1,266 additions and 184 deletions.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"gitlens.telemetry.enabled": false,
"code-runner.enableAppInsights": false
}
57 changes: 34 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# Backend APIs

This repo contains backend APIs for Smart Waste Segregator and Route Planner

### Latest Build Status

![Heroku Deployment WorkFlow](https://github.com/SmartWasteSegregatorAndRoutePlanner/Backend-APIs/actions/workflows/main.yml/badge.svg)
This repo contains backend REST API for Smart Waste Segregator and Route Planner.

## Installation

Expand All @@ -21,18 +17,48 @@ This repo contains backend APIs for Smart Waste Segregator and Route Planner
```

- Install requirements

- Install [Poetry](https://python-poetry.org/docs/)

- Install virtualenv

```bash
python -m pip install virtualenv
```

- Create Virtual env

```bash
python -m virtualenv env
```

- Install dependencies

```bash
poetry install
```

- Create Migrations

```bash
python manage.py makemigrations
```

- Migrate DB

```bash
python -m pip install -r requirements
python manage.py migrate
```

- Start Web Application

```bash
python main.py
python manage.py runserver 0.0.0.0:8000
```

## Installation Error Cases for Windows
> **Note**: Allow Port `8000` through firewall.
## Common Installation Error Fix Cases on Windows

- Check python installed arch version using

Expand All @@ -48,18 +74,3 @@ This repo contains backend APIs for Smart Waste Segregator and Route Planner
```

> Note: Check python version 3.x and cpu arch then download wheel file accordingly
## Run During Development

- Start project with devtools

```bash
adev runserver .
```

## Running with Normal Configuration

```bash
python main.py
```

7 changes: 4 additions & 3 deletions backend_api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from pathlib import Path
from dotenv import load_dotenv, find_dotenv
from os import environ
from os.path import join as path_join

# load .env file
load_dotenv(find_dotenv())
Expand Down Expand Up @@ -43,7 +44,7 @@
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'route_planner',
'route_planner.apps.RoutePlannerConfig',
]

MIDDLEWARE = [
Expand Down Expand Up @@ -137,5 +138,5 @@
]
}

# ENVIRON VARS
ORS_API_KEY = environ.get('ORS_API_KEY')
# to store routes
ROUTES_DATA_FILE_PATH = path_join(BASE_DIR, 'cache', 'routes.json')
2 changes: 1 addition & 1 deletion backend_api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

urlpatterns = [
path('admin/', admin.site.urls),
path('api/route-planner', include('route_planner.urls')),
path('api/route-planner/', include('route_planner.urls')),
]

admin.site.site_header = "Smart Waste Segregation and Route Planner Admin Page"
Expand Down
1,064 changes: 1,001 additions & 63 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ markdown = "^3.4.1"
django-filter = "^22.1"
folium = "^0.14.0"
python-dotenv = "^0.21.0"
openrouteservice = "^2.3.3"
osmnx = "^1.3.0"
scikit-learn = "^1.2.1"


[build-system]
Expand Down
3 changes: 2 additions & 1 deletion route_planner/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.contrib import admin
from .models import GarbageBinLocation

# Register your models here.
admin.site.register(GarbageBinLocation)
11 changes: 10 additions & 1 deletion route_planner/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
from django.db import models

# Create your models here.
class GarbageBinLocation(models.Model):
name = models.CharField(max_length=50)
garbage_weight = models.FloatField(default=0)
latitude = models.FloatField(default=19.2991243)
longitude = models.FloatField(default=72.8780597)
added_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateField(auto_now=True)

def __str__(self) -> str:
return str(self.name)
8 changes: 8 additions & 0 deletions route_planner/serializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from rest_framework import serializers
from .models import GarbageBinLocation

class GarbageBinLocationSerializer(serializers.ModelSerializer):
class Meta:
model = GarbageBinLocation
fields = ['name', 'garbage_weight', 'latitude', 'longitude']

13 changes: 10 additions & 3 deletions route_planner/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
from django.urls import path
from .views import MapView
from django.urls import path, include
from rest_framework.routers import DefaultRouter

from .views import MapViewSet, update_routes_data

router = DefaultRouter()
router.register(r'', MapViewSet, basename='route-planner')


urlpatterns = [
path('', MapView.as_view(), name='route-planner'),
path('', include(router.urls)),
path('update-routes', update_routes_data)
]
116 changes: 58 additions & 58 deletions route_planner/utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from backend_api.settings import ORS_API_KEY
from folium import GeoJson, Map, Marker
from openrouteservice import Client
from openrouteservice.exceptions import ApiError
from openrouteservice.convert import decode_polyline
from backend_api.settings import ROUTES_DATA_FILE_PATH
from json import loads,dumps
from folium import Map, Marker
from html import escape
from os.path import isfile
from .models import GarbageBinLocation

# ors client
ors_client = Client(key=ORS_API_KEY)
import osmnx as ox
import networkx as nx


def to_cordinates(location: str, default: tuple[float] = (28.653458, 77.123767)):
Expand All @@ -22,59 +22,12 @@ def to_cordinates(location: str, default: tuple[float] = (28.653458, 77.123767))
return co_ords


def plot_geojson_data_on_map(route_data:dict, map: Map) -> Map:
# add default value to avoid errs
# routes = geojson.get('routes', [{'geometry': '_zwc@mqrhNLALANhA@HNpAcEv@m@J}A\\wAXqA\\aAPkGZe@BKDA@C?A?KEaAHI?c@@QDMDUHABC?C@CAC?GGAC?C@EGgAOc@ISO[c@SCACCCC_@UOC}@MaAOSAUBa@HCBCBK?ICCCACw@cAGGMKY[w@u@QS}@eAsAiBo@{@]c@MQ_@g@w@kAe@u@gA_CyAmD{@gCk@qBe@kBg@{DGs@E{EE_BESIYSUIGEC]WGSCM@UHo@`@yB@IXiAb@yAl@gBHQ`AkBxAmCt@uAPc@Zo@Zs@@EBGNm@N{@Fq@BS@QHk@Ji@^cA^aAlAwC^aAn@eCF]P}An@eGHa@FSJYVi@n@cAx@}@dB{A|@aAdA_AlA}@l@c@PWROl@e@\\WTQt@s@r@q@JIl@s@Zc@nA_Bb@e@TSDAXO^Y~@m@`Ag@NGZQTMpAy@FHFF?Xw@nBCN@JJDz@RXDNAJEFIBORW\\OXJx@vAL`AFNTTbAf@l@\\~A`@jC^fAHnAV`BZl@FbAJrAD`CJ~BVl@?'}])

# plot routes on map
## decode polyline and add to map
geometry = route_data.get('geometry', 'mtkeHuv|q@kA{@WWIUAU?SDMHMl@[HIDMDYDq@DMLIz@EHCLSBW?MGY_AyBKOECSFe@FQA]EkAWWMk@e@W_@mAmAe@e@WSWW@LJ~C@r@CTYzAeAzEWrBSfA]lAq@zAUp@KXsANQEYNY@WNMAKb@[jAQpAOj@Ab@u@n@]V]LGs@EUa@c@kAm@iBcC_E{D{@_@wAaAa@u@m@sBw@s@yAUEYi@vAh@wADXxATv@r@l@rB`@t@vA`Az@^~DzDhBbCjAl@`@b@DTFr@}@Mk@GuB@c@EsAc@a@Gq@EmBPi@Rq@b@UTI\\ItAE`@TrAAj@BZp@dCPd@R`@x@dEF`AHFHNBh@^~AXvAH`@h@fDGB[XSZyCrFW\\wAx@WTWXQZ_@fAMPUPWJuAVw@XiAh@URYOMGM[kAkEYiB]}@s@sASWUOaA`Bs@j@MPi@pBU^Y\\[\\YXQLCBWLW@WEYM[MGXoArEiAlC_AhCw@|B[p@a@l@g@f@g@XWNmAn@{@V}A\\wATU@UEoAe@aAUiBIWG]MK}@_@mC[oAU{A]w@S]U_@USwAy@OOQSg@u@u@c@m@Ok@A{BJmBXqBkBsA}@{Ao@eB@]GKIMKO_@a@_CQi@AEI]c@iBQi@Qc@kAyBWS_@SaAE{@yAu@mDUsAqA}@EM@QTiA|@iAn@gAd@eAg@_@I]??k@i@yBkEa@}@W}@WkCUqC?_@Hg@ZqABg@Gm@YoAEgAMq@@jAB|CC`@{@rACHBIz@sABa@C}CAkALp@DfAXnAFl@Cf@[pAIf@?^TpCVjCV|@`@|@xBjEj@h@??H\\f@^e@dAo@fA}@hAUhAAPDLpA|@TrAt@lDz@xA`AD^RVRjAxBPb@Ph@b@hBH\\@DPh@`@~BN^LJJH\\FdBAzAn@rA|@pBjBlBYzBKj@@l@Nt@b@f@t@PRNNvAx@TRT^R\\\\v@TzAZnA^lCJ|@\\LVFhBH`ATnAd@TDTAvAU|A]z@WlAo@VOf@Yf@g@`@m@Zq@v@}B~@iChAmCnAsEFYZLXLVDVAVMBCPMXYZ]X]T_@h@qBLQr@k@`AaBTNRVr@rA\\|@XhBjAjELZLFXNTShAi@v@YtAWVKTQLQ^gAP[VYVUvAy@V]xCsFR[ZYFCL?VDh@D\\AZK`Ak@`A}@ZUVKZGtAGnA@f@APC`@Ib@SXUl@c@bAy@r@i@Z[^e@lAaC\\i@NMVInAQTMPSn@aBh@u@VSVKZ?zBd@X?VGr@_@nAgBjAsAL_@x@kDVaALe@No@@_ABq@Da@Lu@Vk@RYf@a@b@UdGgCz@_APKLKDEd@[`@k@DIx@qAf@kARiAFSFSt@cBLQ^e@TI\\BL{CFo@t@iFFgA?cBCsA[mFBwBLeAjA{Cj@aBTsADq@@i@QeF?k@N{@Rg@V_@^]NKRQXShAeAn@gBZwB\\{DJkB?m@?_AKyBAeCtAeG~AuGd@qA^i@\\o@Vk@Ja@@c@@e@I_Ai@aBEOKe@Oq@CUA[@[BQJo@NWNUd@OPAVBpAv@ZJVBRCTILIb@o@HUP{@@y@Aa@U}@eDoFMWGSKa@O_AMeBUgEFsAJi@Nc@N]X_@rCuCjAcBr@uALk@N_AFwA@]?WC]Eo@eA_FGc@M}@_BcO?_@LUH]BWAs@Q{@m@{A[q@M[a@gAKUWu@IWEQEMIs@KgBAMGqAGqAs@Dc@B[BcAFg@FWHIFOTqAxBKFI@I?QEKCOAa@@k@RGDA?MBQCg@OOE{Ae@_A[k@O}@Yu@GUcAEi@Ca@AW@q@@yA@W?{@IeBK{B?sA?iAJiDGyAOgCGqACw@G_B?k@V_A@WAWOg@k@_ASg@g@eBYgAO}@K{@IuAGcECqC?mDBo@n@iELeBNm@V_@d@w@D]B[Ai@G{@Ig@?e@J{AjAaLd@kDLmA\\wF@y@IaAiA_GSoAOeBIeBA{D@g@?k@Dg@Lq@fAyBDQBkAAi@Aa@j@CjADfAz@d@hA\\bB\\vAVl@VNJH?eFEuH?yBIqCGiCBcAB[Fe@P_AVsAFc@\\cCH{@BYB[?c@AMf@\\l@`@ZTDBf@\\TNl@`@BBj@\\TPVPp@h@rAhA`B~AZ\\T\\T`@PZLPR\\ZXf@L^EbCeBVe@Vw@t@{CP]RSRQlB}@l@On@Fb@XVVbBrBpCrCrCdEFFFLtA~AlB`Dt@p@x@Z?TCj@`BdB|ApB')
decoded = decode_polyline(geometry)

print('DECODED:', decoded)
GeoJson(decoded).add_to(map)

return map


def get_route_data(cordinates: list[list[float]]):
assert isinstance(cordinates, list)
global ors_client

# sanitize co-ordinates to prevent injection attacks
# and create co-ordinates in [long, lat] order from
# [lat, long] order. delete any co-ordinate if it is
# invalid
for co_ordinate in cordinates:
try:
lat = float(co_ordinate[0])
long = float(co_ordinate[1])
co_ordinate = [long, lat]
except ValueError:
del co_ordinate

# make request and get directions geojson data
status = 200
try:
routes_data = ors_client.directions(cordinates, profile='driving-car') # radiuses=[-1,-1], optimize_waypoints=True
print('ORG R data:',routes_data)
routes_data = routes_data.get('routes', [{}])[0]
except KeyError as e:
routes_data = {'error':e}
except ApiError as e:
routes_data = dict(e.message).get('error', {
'error': 'try co-ordinates with shorter distance'
})
status = 404

return routes_data, status


def add_markers_to_map(co_ordinates: list[list[float]], map: Map):
for coordinate in co_ordinates:
def add_locations_to_map(locations: list[GarbageBinLocation], map: Map):
for location in locations:
coordinate = (location.latitude, location.longitude)
Marker(
location=coordinate,
# tooltip='',
tooltip=location.name,
popup=str(coordinate)
).add_to(map)

Expand All @@ -83,3 +36,50 @@ def add_markers_to_map(co_ordinates: list[list[float]], map: Map):

def sanitize(data: str) -> str:
return escape(str(data))


def get_shortest_distance(start_loc:GarbageBinLocation, end_loc:GarbageBinLocation):
'''
Returns shortest distance between two Garbage Bin locations
'''
start_latlng = (start_loc.latitude, start_loc.longitude)
end_latlng = (end_loc.latitude, end_loc.longitude)

mode = 'drive' # 'drive', 'bike', 'walk'# find shortest path based on distance or time
optimizer = 'length' # 'length','time'

# create graph from point
graph = ox.graph_from_point(
center_point=start_latlng, dist=4000, network_type=mode)

# find the nearest node to the end location
orig_nodes = ox.nearest_nodes(
graph, X=start_latlng[1], Y=start_latlng[0])
dest_nodes = ox.nearest_nodes(
graph, X=end_latlng[1], Y=end_latlng[0]) # find the shortest path

try:
shortest_route = nx.shortest_path(
graph,
orig_nodes,
dest_nodes,
weight=optimizer
)
except nx.exception.NetworkXNoPath:
shortest_route = None

return shortest_route


def save_routes(routes:dict={}):
with open(ROUTES_DATA_FILE_PATH, 'w') as f:
f.write(dumps(routes))

def read_routes() -> dict:
# create empty json data if file does
# not exist
if not isfile(ROUTES_DATA_FILE_PATH):
save_routes()

with open(ROUTES_DATA_FILE_PATH, 'r') as f:
return loads(f.read())
Loading

0 comments on commit 45349c1

Please sign in to comment.