Skip to content

Commit

Permalink
Merge branch 'main' into 160-refactor-graphs-for-json
Browse files Browse the repository at this point in the history
  • Loading branch information
kdumais111 committed May 22, 2024
2 parents d5b0dbc + 2c6d03d commit 5e34555
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 50 deletions.
Binary file added app/route_rangers_api/static/images/map_pin_W.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 44 additions & 3 deletions app/route_rangers_api/static/map.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function initializeMap(coordinates, stations, iconUrl, routes) {
export function initializeMap(coordinates, stations, iconUrl, userIconUrl, routes, userDrawn) {

// Add a tile layer
var tileLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
Expand All @@ -12,13 +12,19 @@ export function initializeMap(coordinates, stations, iconUrl, routes) {
// Initialize the map at center of city
var map = L.map('map', { layers: [tileLayer] }).setView(coordinates, 13);

// Custom icon for smaller markers
// Custom icons for smaller markers
var smallIcon = L.icon({
iconUrl: iconUrl, // URL to a smaller icon image
iconSize: [15, 15],
iconAnchor: [6, 6],
});

var userIcon = L.icon({
iconUrl: userIconUrl, // URL to a smaller icon image
iconSize: [20, 20],
iconAnchor: [9, 9],
});

// Set level of zoom at which each kind of transit stop declusters,
// based on how spaced apart they usually are
var zoomEnd = {
Expand Down Expand Up @@ -98,12 +104,47 @@ export function initializeMap(coordinates, stations, iconUrl, routes) {
}
});

// Add user-drawn route and endpoint layers

routeLayers["User-drawn"] = L.layerGroup();
markerClusterGroups["User-drawn"] = L.markerClusterGroup();

L.geoJSON(userDrawn, {
onEachFeature: function (feature, layer) {

var geometries = feature.geometry.geometries;
geometries.forEach(function (geometry) {
if (geometry.type === "LineString") {
var user_route = L.polyline(geometry.coordinates, { color: "#aaaacc", opacity: 0.6, });
routeLayers["User-drawn"].addLayer(user_route);
} else if (geometry.type === "Point") {
var coords = geometry.coordinates;
if (coords && coords.length === 2) {
// TODO: figure out why the coords need to be reversed here and not above
var x = coords[1];
var y = coords[0];
if (x != undefined && y != undefined) {
var marker = L.marker([x, y], { icon: userIcon });
marker.bindTooltip("User-submitted endpoint");
markerClusterGroups["User-drawn"].addLayer(marker);
} else {
console.error("Invalid coordinates for Point:", coordinates);
}
} else {
console.error("Invalid coordinates array for Point:", coords);
}

}
});
}
});

// Adjust width of routes with zoom level
function updateRouteWidth() {
var zoom = map.getZoom();
for (var rType in routeLayers) {
routeLayers[rType].eachLayer(function (layer) {
layer.setStyle({ weight: (zoom / 4) - 1 });
layer.setStyle({ weight: (zoom / 4) - 0.5 });
});
}
}
Expand Down
47 changes: 11 additions & 36 deletions app/route_rangers_api/templates/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,19 @@ <h1>{{ City }} Transit Ecosystem</h1>
</div>
<div class="card-container row">
<div class="col-12 col-md-3 col-lg-3">
<button
class="btn btn-secondary-subtle dropdown-toggle"
type="button"
id="typeDropdown"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<button class="btn btn-secondary-subtle dropdown-toggle" type="button" id="typeDropdown" data-bs-toggle="dropdown"
aria-expanded="false">
Select Transit Type
</button>
<ul class="dropdown-menu" aria-labelledby="typeDropdown">
<li>
<a
class="dropdown-item"
href="#"
onclick="updateCards('typeDropdown', this)"
>All</a
>
<a class="dropdown-item" href="#" onclick="updateCards('typeDropdown', this)">All</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
onclick="updateCards('typeDropdown', this)"
>Bus</a
>
<a class="dropdown-item" href="#" onclick="updateCards('typeDropdown', this)">Bus</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
onclick="updateCards('typeDropdown', this)"
>Train</a
>
<a class="dropdown-item" href="#" onclick="updateCards('typeDropdown', this)">Train</a>
</li>
<!-- Add more months as needed -->
</ul>
Expand Down Expand Up @@ -71,7 +51,7 @@ <h1>{{ City }} Transit Ecosystem</h1>
</div>
<script>
cityData = {{ citydata | safe }};
var type= "All";
var type = "All";
document.getElementById("totalRidersText").textContent =
cityData[type]["TotalRiders"];
document.getElementById("totalRoutesText").textContent =
Expand Down Expand Up @@ -124,11 +104,7 @@ <h2>Heat Map</h2>
Maps of showing variation in transit, environmental, and socio-economic
variables across census tracts <br />
</p>
<div
id="heatmap"
class="mx-auto"
style="width: 90%; height: 600px; margin-top: 5%"
></div>
<div id="heatmap" class="mx-auto" style="width: 90%; height: 600px; margin-top: 5%"></div>
</div>

<div class="headers">
Expand Down Expand Up @@ -160,9 +136,11 @@ <h2>Trends Over Time</h2>
var coordinates = {{ coordinates }};
var stations = {{ stations | safe }};
var iconUrl = "{% static 'images/map_pin.png' %}";
var userIconUrl = "{% static 'images/map_pin_W.png' %}";
var routes = {{ routes | safe}};
var userDrawn = {{ user_drawn | safe }};
// "safe" parameter needed to deal with escaped quotation marks in GeoJSON
initializeMap(coordinates, stations, iconUrl, routes);
initializeMap(coordinates, stations, iconUrl, userIconUrl, routes, userDrawn);
</script>

<!-- Map and graph Imports-->
Expand All @@ -174,6 +152,7 @@ <h2>Trends Over Time</h2>
<script src="{% static 'heatmap.js' %}"></script>
<script src="{% static 'cards.js' %}"></script>
<script>

const top_subway_week= {{top_subway_weekday | safe}};
const top_subway_wked= {{top_subway_weekend | safe}};
const top_bus_week= {{top_bus_weekday | safe}};
Expand All @@ -184,14 +163,10 @@ <h2>Trends Over Time</h2>
drawhorizontalgraph(top_bus_week, "name","avg_ridership", "#toptenbus", "#6F8695");
drawTrends(daily_riderships, ridership_labels)


var heatmap_categories = {{ heatmap_categories | safe }};
var heatmap_units = {{ heatmap_units | safe }};
var heatmap_titles = {{ heatmap_titles | safe }};
var heatmap_titles_reversed = {{ heatmap_titles_reversed | safe }};

heatmaps("{{geojsonfilepath}}", {{coordinates}}, '{{heatmaplabel}}', heatmap_categories, heatmap_units, heatmap_titles, heatmap_titles_reversed);
</script>
{% endblock %}


3 changes: 1 addition & 2 deletions app/route_rangers_api/templates/responses.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,5 @@ <h2>Spatial Analysis</h2>
drawhorizontalgraph(tran_feedback,"transit_type","count", "#dataviz", "#566C4B")
drawgraph(transitform,"transit_type","count", "#tranform", "#6F8695")
drawgraph(tod,"tod","count", "#timeofday", "#BF5002")

</script>
{% endblock %}
{% endblock %}
1 change: 1 addition & 0 deletions app/route_rangers_api/utils/metric_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ def extract_top_ten(
routes = routes.filter(date__week_day__in=[1, 2, 3, 4, 5])
else:
routes = routes.exclude(Q(date__week_day=1) | Q(date__week_day=7))

top_ten_routes = (
routes.values("route_id__route_name")
.annotate(avg_ridership=Sum("ridership") / Count("ridership"))
Expand Down
15 changes: 8 additions & 7 deletions app/route_rangers_api/utils/survey_results_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ def get_rider_satisfaction(city: str) -> float:

return round(average, 1)


def get_transit_mode(city: str) -> Dict:
"""
Given a city, return a dictionary with a count
Expand All @@ -103,6 +102,7 @@ def get_transit_mode(city: str) -> Dict:
}

mode_count=[]

for mode_id, mode_name in MODES_OF_TRANSIT.items():
count_by_mode = SurveyResponse.objects.filter(
city=CITY_CONTEXT[city]["DB_Name"], modes_of_transit=mode_id
Expand All @@ -117,8 +117,10 @@ def get_trip_top(city: str) -> Dict:
Given a city, return a dictionary with a count
of responses by time of day
"""

TIME_OF_DAY = {1: "Peak Commute Hours", 2: "Daytime", 3: "Night"}
tod_count=[]

for tod_id, tod_name in TIME_OF_DAY.items():
count_by_tod = SurveyResponse.objects.filter(
city=CITY_CONTEXT[city]["DB_Name"], trip_tod=tod_id
Expand All @@ -140,6 +142,7 @@ def get_transit_improv_drivers_dict(city: str) -> Dict:
4: "It feels safe at the station and onboard",
5: "No improvement needed",
}

improv = []
for improv_id, improv_name in TRANSIT_IMPROVEMENT.items():
count_by_improv = SurveyResponse.objects.filter(
Expand All @@ -148,10 +151,8 @@ def get_transit_improv_drivers_dict(city: str) -> Dict:
user_id_id__car_owner=1, # Filter by car_owner
).count()
improv.append({"transit_type": improv_name,"count": count_by_improv})

return improv


def get_transit_improv_riders_dict(city: str) -> Dict:
"""
Given a city, return a dictionary with a count
Expand All @@ -164,14 +165,14 @@ def get_transit_improv_riders_dict(city: str) -> Dict:
4: "It feels safe at the station and onboard",
5: "No improvement needed",
}

improv = []

for improv_id, improv_name in TRANSIT_IMPROVEMENT.items():
count_by_improv = SurveyResponse.objects.filter(
city=CITY_CONTEXT[city]["DB_Name"],
transit_improvement=improv_id,
user_id_id__car_owner=2, # Filter by car_owner
).count()
improv.append({"transit_type": improv_name,"count": count_by_improv})


return improv
improv.append({"transit_type": improv_name,"count": count_by_improv}
return improv
58 changes: 56 additions & 2 deletions app/route_rangers_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.urls import reverse
from django.core.serializers import serialize
from django.templatetags.static import static
from django.contrib.gis.geos import GEOSGeometry, MultiLineString, LineString
from django.contrib.gis.geos import GEOSGeometry, MultiLineString, LineString, Point
from django.views.decorators.cache import cache_page
import uuid
import json
Expand Down Expand Up @@ -57,7 +57,7 @@ def about(request):
# caching for 6 hours since the data doesn't change often
@cache_page(60 * 60 * 6)
def dashboard(request, city: str):
# Get Routes:
# Get existing routes and stations
routes = TransitRoute.objects.filter(city=CITY_CONTEXT[city]["DB_Name"])
# reduce load time and data transfer size by overwriting model attribute
TOLERANCE = 0.00005
Expand All @@ -83,6 +83,58 @@ def dashboard(request, city: str):
"geojson", stations, geometry_field="location", fields=("station_name", "mode")
)

# Get user-drawn routes and endpoints
user_routes = SurveyResponse.objects.filter(
city=CITY_CONTEXT[city]["DB_Name"],
route__isnull=False,
starting_point__isnull=False,
end_point__isnull=False,
)
# reduce load time and data transfer size by overwriting model attribute
user_features = []
for user_drawn in user_routes:
simple_route = user_drawn.route.simplify(
tolerance=TOLERANCE, preserve_topology=True
)
user_drawn.route = simple_route
user_features.append(
{
"type": "Feature",
"geometry": {
"type": "GeometryCollection",
"geometries": [
{
"type": "LineString",
"coordinates": [
# TODO: figure out why these are reversed y,x
# instead of x,y
[coord[1], coord[0]]
for coord in user_drawn.route.coords
],
},
{
"type": "Point",
"coordinates": [
user_drawn.starting_point.x,
user_drawn.starting_point.y,
],
},
{
"type": "Point",
"coordinates": [
user_drawn.end_point.x,
user_drawn.end_point.y,
],
},
],
},
"properties": {"id": user_drawn.id},
}
)

# When you make a geoJSON yourself, you don't need to serialize it
user_drawn_json = {"type": "FeatureCollection", "features": user_features}

city_name = CITY_CONTEXT[city]["CityName"]

# Get dashboard metrics:
Expand All @@ -100,6 +152,7 @@ def dashboard(request, city: str):
mode=1,
transit_unit=CITY_RIDERSHIP_LEVEL[city]["subway"],
weekday=False,

)
top_bus_weekday = extract_top_ten(
city=city, mode=3, transit_unit=CITY_RIDERSHIP_LEVEL[city]["bus"], weekday=True
Expand Down Expand Up @@ -162,6 +215,7 @@ def dashboard(request, city: str):
"Percent of people who commute via subway": "percentage_public_to_work",
"Population": "population",
},
"user_drawn": user_drawn_json,
}

return render(request, "dashboard.html", context)
Expand Down

0 comments on commit 5e34555

Please sign in to comment.