Skip to content

Commit

Permalink
Merge branch 'deploy' into min-bins
Browse files Browse the repository at this point in the history
  • Loading branch information
Nate-Wessel committed Jan 9, 2025
2 parents b9de246 + 4d3bbba commit ee28261
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 31 deletions.
32 changes: 32 additions & 0 deletions backend/app/nodes/byID/centreline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import json
from app.db import getConnection
from app.nodes.conflation import add_conflated_nodes

SQL = '''
SELECT
ST_AsGeoJSON(geom) AS geojson,
ARRAY_AGG(DISTINCT linear_name_full_from) AS street_names
FROM gis_core.intersection_latest
WHERE intersection_id = %(node_id)s
GROUP BY geom;
'''

def get_centreline_node(node_id, doConflation=False):
"""fetch a specific centreline node by it's ID"""
node = {}
with getConnection() as connection:
with connection.cursor() as cursor:
cursor.execute(SQL, {"node_id": node_id})
if cursor.rowcount != 1:
return None
geojson, street_names = cursor.fetchone()
node = {
'node_id': node_id,
'network': 'centreline',
'street_names': street_names,
'geometry': json.loads(geojson)
}
connection.close()
if doConflation:
node = add_conflated_nodes(node)
return node
15 changes: 7 additions & 8 deletions backend/app/get_node.py → backend/app/nodes/byID/here.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import json
from app.db import getConnection
from app.get_nearest_centreline_node import get_nearest_centreline_node
from app.nodes.conflation import add_conflated_nodes

SQL = '''
SELECT
Expand All @@ -18,22 +18,21 @@
here_nodes.geom;
'''

def get_node(node_id, conflate_with_centreline=False):
def get_here_node(node_id, conflate_with_centreline=False):
node = {}
with getConnection() as connection:
with connection.cursor() as cursor:
cursor.execute(SQL, {"node_id": node_id})
if cursor.rowcount != 1:
return None
geojson, street_names = cursor.fetchone()
node = {
'node_id': node_id,
'network': 'here',
'street_names': street_names,
'geometry': json.loads(geojson)
}
if conflate_with_centreline:
lon = node['geometry']['coordinates'][0]
lat = node['geometry']['coordinates'][1]
node['conflated'] = {
'centreline': get_nearest_centreline_node(lon, lat)
}
connection.close()
if conflate_with_centreline:
node = add_conflated_nodes(node)
return node
35 changes: 35 additions & 0 deletions backend/app/nodes/byID/px.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
from app.db import getConnection

SQL = '''
SELECT
tts.px,
ST_AsGeoJSON(tts.geom) AS geojson,
array_remove(
ARRAY[
InitCap(gts.main_street),
InitCap(gts.side1_street),
InitCap(gts.side2_street)
],
NULL
) AS street_names
FROM traffic.traffic_signal AS tts
JOIN gis.traffic_signal AS gts ON tts.px = gts.px::int
WHERE tts."centrelineId" = %(centreline_id)s;
'''

def get_px_node(centreline_id):
node = {}
with getConnection() as connection:
with connection.cursor() as cursor:
cursor.execute(SQL, {"centreline_id": centreline_id})
if cursor.rowcount != 1:
return None
px, geojson, street_names = cursor.fetchone()
node = {
'node_id': px,
'network': 'px',
'street_names': street_names,
'geometry': json.loads(geojson)
}
return node
43 changes: 43 additions & 0 deletions backend/app/nodes/conflation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from app.nodes.nearby.here import get_here_nodes_within
from app.nodes.byID.px import get_px_node
from app.nodes.nearby.centreline import get_nearest_centreline_node
from haversine import haversine

def add_conflated_nodes(node):
"""adds "conflated" field to node objects"""

node['conflated'] = {}
lon = node['geometry']['coordinates'][0]
lat = node['geometry']['coordinates'][1]

if node['network'] == 'centreline':
# adds px and here nodes
# px search is based on the centreline_id
node['conflated']['px'] = get_px_node(node['node_id'])
try:
# here search is based on distance
node['conflated']['here'] = get_here_nodes_within(50, lon, lat, 1)[0]
except:
pass
elif node['network'] == 'here':
# adds centreline and px nodes
# get centreline by nearest
node['conflated']['centreline'] = get_nearest_centreline_node(lon, lat)
# get px from Id of nearest centreline
node['conflated']['px'] = get_px_node(node['conflated']['centreline']['node_id'])

# now get distances between selected and conflated points
for network, conflatedNode in node['conflated'].items():
try:
conflatedNode['distance'] = haversine(
(lat, lon),
(
conflatedNode['geometry']['coordinates'][1],
conflatedNode['geometry']['coordinates'][0]
),
unit='m'
)
except:
pass

return node
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ def get_nearest_centreline_node(longitude, latitude):
cursor.execute(SQL, {'longitude': longitude, 'latitude': latitude})
centreline_id, geojson, distance, street_names = cursor.fetchone()
node = {
'centreline_id': centreline_id,
'centreline_id': centreline_id, # deprecated
'node_id': centreline_id,
'network': 'centreline',
'street_names': street_names,
'geometry': loadJSON(geojson),
'distance': distance
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"""Return intersection(s) near a provided coordinate"""

import json
from app.db import getConnection

Expand All @@ -16,18 +14,24 @@
cg_nodes.node_id,
cg_nodes.geom
ORDER BY distance
LIMIT 20;
LIMIT %(limit)s;
'''

def get_nodes_within(meters,longitude, latitude):
def get_here_nodes_within(meters, longitude, latitude, limit=20):
"""
Return intersection(s) near a provided coordinate
will only give nodes on the congestion network
"""
with getConnection() as connection:
with connection.cursor() as cursor:
cursor.execute(SQL, {"latitude": latitude, "longitude": longitude})
cursor.execute(SQL, {"latitude": latitude, "longitude": longitude, 'limit': limit})
candidate_nodes = []
for node_id, geojson, distance, street_names in cursor.fetchall():
if distance <= meters:
candidate_nodes.append( {
'node_id': node_id,
'network': 'here',
'street_names': street_names,
'geometry': json.loads(geojson)
} )
Expand Down
38 changes: 23 additions & 15 deletions backend/app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
from flask import jsonify, request
from app import app
from app.db import getConnection
from app.get_closest_nodes import get_nodes_within
from app.get_node import get_node
from app.nodes.nearby.here import get_here_nodes_within
from app.nodes.byID.here import get_here_node
from app.nodes.byID.centreline import get_centreline_node
from app.get_travel_time import get_travel_time
from app.get_here_links import get_here_links
from app.get_centreline_links import get_centreline_links
Expand Down Expand Up @@ -32,7 +33,7 @@ def version():
'git-HEAD': getGitHash()
})

# test URL /closest-node/-79.3400/43.6610
# test URL /nodes-within/50/-79.3400/43.6610
@app.route('/nodes-within/<meters>/<longitude>/<latitude>', methods=['GET'])
def closest_node(meters, longitude, latitude):
"""Return up to 20 nodes within a given radius (in meters) of a point.
Expand All @@ -50,29 +51,36 @@ def closest_node(meters, longitude, latitude):
meters = float(meters)
except:
return jsonify({'error': "all inputs must be decimal numbers"})
return jsonify(get_nodes_within(meters,longitude,latitude))

# test URL /node/30357505
@app.route('/node/<node_id>', methods=['GET'])
def node(node_id):
"""Returns information about a given node in the Here street network.
This uses the latest map version and may not recognize an older node_id.
return jsonify(get_here_nodes_within(meters,longitude,latitude))

# test URL /node/here/30357505
@app.route('/node/<node_id>', endpoint='generic') # will be deprecated
@app.route('/node/here/<node_id>', endpoint='here')
@app.route('/node/centreline/<node_id>', endpoint='centreline')
def get_node(node_id):
"""Returns information about a given node in the either the Here or
Centreline street networks.
This uses the latest version of either network and may not recognize an
older node_id.
arguments:
node_id (int): identifier of the node in the latest Here map version
optional GET arg ?doConflation will also return the nearest node in the centreline network
optional GET arg ?doConflation will also return the nearest node in the other
networks as well as their distance in meters from the main selected node
"""
try:
node_id = int(node_id)
except:
return jsonify({'error': "node_id should be an integer"})

doConflation = False
if request.args.get('doConflation') is not None:
doConflation = True

return jsonify(get_node(node_id, doConflation))
if request.endpoint == 'centreline':
node = get_centreline_node(node_id, doConflation)
else: # here network
node = get_here_node(node_id, doConflation)
return jsonify(node if node else {'error': 'node not found'})

# test URL /link-nodes/here/30421154/30421153
#shell function - outputs json for use on frontend
Expand Down
3 changes: 2 additions & 1 deletion backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ pandas==2.2.2
psycopg==3.1.18
python-dotenv==0.15.0
numpy==1.26.0
git+ssh://[email protected]/Toronto-Big-Data-Innovation-Team/[email protected]
git+ssh://[email protected]/Toronto-Big-Data-Innovation-Team/[email protected]
haversine==2.9.0
2 changes: 1 addition & 1 deletion frontend/src/Sidebar/restoreStateFromFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function restoreStateFromFile(fileDropEvent,stateData,logActivity){
let corridor = stateData.createCorridor()
Promise.all(
[startNode,endNode].map(node_id => {
return fetch(`${domain}/node/${node_id}`)
return fetch(`${domain}/node/here/${node_id}`)
.then( resp => resp.json() )
.then( node => new Intersection( {
id: node.node_id,
Expand Down

0 comments on commit ee28261

Please sign in to comment.