Skip to content

Commit

Permalink
DBC22-1377 FE rendering, BE prep
Browse files Browse the repository at this point in the history
  • Loading branch information
fatbird authored and ray-oxd committed Dec 22, 2023
1 parent e467b18 commit 057b86b
Show file tree
Hide file tree
Showing 21 changed files with 167 additions and 15 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@
src/media/
src/static/
infrastructure/yaml

src/backend/test_script.py
8 changes: 8 additions & 0 deletions src/backend/apps/feed/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@
WEBCAM = "webcam"
OPEN511 = "open511"
INLAND_FERRY = "inland_ferry"

DIRECTIONS = {
'in both directions': 'BOTH',
'northbound': 'N',
'southbound': 'S',
'eastbound': 'E',
'westbound': 'W',
}
12 changes: 12 additions & 0 deletions src/backend/apps/feed/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def to_internal_value(self, data):
}
return res

def to_representation(self, value):
return super().to_representation(value[self.custom_field_name])


class DriveBCSingleListField(DriveBCField):
def to_internal_value(self, data):
Expand Down Expand Up @@ -46,6 +49,8 @@ def to_internal_value(self, value):

return res

def to_representation(self, value):
return super().to_representation(value[self.custom_field_name])

# Webcam
class WebcamRegionField(serializers.Field):
Expand Down Expand Up @@ -110,9 +115,16 @@ def to_internal_value(self, data):
"route_from": route_dict["from"],
"route_to": route_dict["to"] if "to" in route_dict else "",
"direction": route_dict["direction"]
# "route_at": data["name"],
# "route_from": data["from"],
# "route_to": data["to"] if "to" in data else "",
# "direction": data["direction"]
}
return res

def to_representation(self, value):
return f'{value["route_from"]} to {value["route_to"]}, {value["direction"]}'


class EventGeographyField(DriveBCField, GeometryField):
pass
Expand Down
36 changes: 35 additions & 1 deletion src/backend/apps/feed/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from datetime import datetime
import pytz

from rest_framework import serializers

from apps.event.enums import EVENT_STATUS
from apps.feed.constants import DIRECTIONS
from apps.feed.fields import (
DriveBCDateField,
DriveBCField,
Expand All @@ -14,7 +19,6 @@
WebcamRegionField,
WebcamRegionGroupField,
)
from rest_framework import serializers


# Webcam
Expand Down Expand Up @@ -57,6 +61,7 @@ class EventFeedSerializer(serializers.Serializer):
event_type = serializers.CharField(max_length=32)
event_subtypes = DriveBCSingleListField('event_sub_type',
source="*", required=False)
# event_sub_type = serializers.CharField(max_length=32, required=False)

# General status
status = serializers.CharField(max_length=32)
Expand All @@ -69,11 +74,30 @@ class EventFeedSerializer(serializers.Serializer):
# Update status
created = DriveBCDateField('first_created', source="*")
updated = DriveBCDateField('last_updated', source="*")
# closed = serializers.SerializerMethodField()

# Schedule
schedule = serializers.JSONField()

def to_internal_value(self, data):
# mapping CARS API fields to Open511 fields
# data['id'] = data["event-id"]
# details = data.get('open511-event-details', {})
# data['event_type'] = details['event_type_description']
# data['event_sub_type'] = details['event_subtype']
# data['severity'] = data['representation']['priority']['name'].upper()
# data['updated'] = datetime.fromtimestamp(data['update-time']['time']/1000,
# pytz.timezone(data['update-time']['timeZoneId'])).isoformat()
# data['created'] = data['updated'] # hack because CARS API doesn't include event creation time
# data['status'] = EVENT_STATUS.ACTIVE
# data['roads'] = {
# 'to': details['event_road_to'],
# 'from': details['event_road_from'],
# 'name': 'Other roads',
# 'direction': DIRECTIONS.get(details['event_road_direction'], 'NONE')
# }
# data['geography'] = data['geometry']

internal_data = super().to_internal_value(data)
schedule = internal_data.get('schedule', {})
if 'intervals' in schedule:
Expand All @@ -88,6 +112,16 @@ def to_internal_value(self, data):

return internal_data

def get_closed(self, obj):
for detail in self.initial_data.get('details', []):
for desc in detail.get('descriptions', []):
kind = desc.get('kind', {})
if (kind.get('category') == 'traffic_pattern' and
kind.get('code') == 'closed'):
return True

return False


class EventAPISerializer(serializers.Serializer):
events = EventFeedSerializer(many=True)
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*

src/backend/test_script.py
1 change: 1 addition & 0 deletions src/frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function App() {
? JSON.parse(context)
: {
visible_layers: {
closures: true,
majorEvents: true,
minorEvents: true,
futureEvents: true,
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ body {
}
}

&.major {
&.major, &.closure {
.popup__title {
background-color: $Surface-status-red;
.name {
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/src/Components/EventTypeIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import {
faPersonDigging,
faCalendarDays,
faSnowflake,
faMinusCircle,
} from "@fortawesome/free-solid-svg-icons";

export default function EventTypeIcon({ displayCategory }) {
switch (displayCategory) {
case "closure":
return <FontAwesomeIcon icon={faMinusCircle} alt="closure" />;
case "majorEvents":
return <FontAwesomeIcon icon={faExclamationTriangle} alt="major delays" />;
case "minorEvents":
Expand Down
31 changes: 31 additions & 0 deletions src/frontend/src/Components/Layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ import './Layers.scss';
export default function Layers({ open, setLayersOpen, toggleLayer }) {
const { mapContext } = useContext(MapContext);

const tooltipClosures = (
<Tooltip id="tooltip" className="tooltip-content">
<p>Indicates a complete closure of the road at this point or segment.</p>
</Tooltip>
);

const tooltipMajor = (
<Tooltip id="tooltip" className="tooltip-content">
<p>Indicates a significant delay of more than 20 minutes to travel in at least one direction on this road. A Major Delay may be a traffic incident or a road event (such as road work, construction, or restoration).</p>
Expand Down Expand Up @@ -77,6 +83,7 @@ export default function Layers({ open, setLayersOpen, toggleLayer }) {
// );

// States for toggles
const [closures, setClosures] = useState(mapContext.visible_layers.closures);
const [majorEvents, setMajorEvents] = useState(mapContext.visible_layers.majorEvents);
const [minorEvents, setMinorEvents] = useState(mapContext.visible_layers.minorEvents);
const [futureEvents, setFutureEvents] = useState(mapContext.visible_layers.futureEvents);
Expand Down Expand Up @@ -118,6 +125,30 @@ export default function Layers({ open, setLayersOpen, toggleLayer }) {
<p className="filter-group__title">Delays</p>
<div className="filter-items-group">
<div className="filter-items filter-items--delays">
<div className={'filter-item filter-item--closures' + (closures ? ' checked' : '')}>
<div className="filter-item__icon">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path className="customIcon-bg" d="M7.87872 1.12135C9.05029 -0.0502183 10.9498 -0.0502166 12.1214 1.12136L18.8787 7.87868C20.0503 9.05026 20.0503 10.9498 18.8787 12.1213L12.1213 18.8787C10.9497 20.0503 9.05026 20.0503 7.87868 18.8787L1.12136 12.1214C-0.0502174 10.9498 -0.0502174 9.05029 1.12136 7.87872L7.87872 1.12135Z" fill="#1A5A96"/>
<path className="customIcon-fg" d="M10.8176 5.71429V11.4286C10.8176 11.8304 10.4403 12.1429 10.0126 12.1429C9.55976 12.1429 9.20755 11.8304 9.20755 11.4286V5.71429C9.20755 5.33483 9.55976 5.00001 10.0126 5.00001C10.4403 5.00001 10.8176 5.33483 10.8176 5.71429ZM10.0126 15C9.63523 15 9.30818 14.8438 9.13208 14.5536C8.95598 14.2857 8.95598 13.9509 9.13208 13.6607C9.30818 13.3929 9.63523 13.2143 10.0126 13.2143C10.3648 13.2143 10.6918 13.3929 10.8679 13.6607C11.044 13.9509 11.044 14.2857 10.8679 14.5536C10.6918 14.8438 10.3648 15 10.0126 15Z" fill="white"/>
</svg>
</div>
<input
type="checkbox"
name="closures"
id="filter--closures"
onChange={e => {
toggleLayer('closures', e.target.checked);
toggleLayer('closuresLines', e.target.checked);
setClosures(!closures)
}}
defaultChecked={mapContext.visible_layers.closures}
/>
<label htmlFor="filter--closures">Closures</label>
<OverlayTrigger placement="top" overlay={tooltipClosures}>
<span className="tooltip-info">?</span>
</OverlayTrigger>
</div>

<div className={'filter-item filter-item--major' + (majorEvents ? ' checked' : '')}>
<div className="filter-item__icon">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
Expand Down
11 changes: 10 additions & 1 deletion src/frontend/src/Components/data/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ export function getEvents(routePoints) {
const payload = routePoints ? { route: routePoints } : {};

return get(`${window.API_HOST}/api/events/`, payload)
.then((data) => data)
.then((data) => {
data.forEach((datum) => {
datum.roadIsClosed = !! datum.description.match(/Road closed(\.| )/);
if (datum.roadIsClosed) {
datum.severity = 'CLOSURE';
datum.display_category = 'closure';
}
})
return data;
})
.catch((error) => {
console.log(error);
});
Expand Down
34 changes: 34 additions & 0 deletions src/frontend/src/Components/data/featureStyleDefinitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Icon, Stroke, Style } from 'ol/style.js';
import cameraIconActive from '../../images/mapIcons/camera-active.png';
import cameraIconHover from '../../images/mapIcons/camera-hover.png';
import cameraIconStatic from '../../images/mapIcons/camera-static.png';
import closedIconActive from '../../images/mapIcons/closure-active.png';
import closedIconHover from '../../images/mapIcons/closure-hover.png';
import closedIconStatic from '../../images/mapIcons/closure-static.png';
import ferryIconActive from '../../images/mapIcons/ferry-active.png';
import ferryIconHover from '../../images/mapIcons/ferry-hover.png';
import ferryIconStatic from '../../images/mapIcons/ferry-static.png';
Expand Down Expand Up @@ -61,6 +64,37 @@ export const cameraStyles = {
}),
};

// Road closed icon styles
export const closureStyles = {
static: new Style({
image: new Icon({
anchor: [88, 88],
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
scale: 0.25,
src: closedIconStatic,
}),
}),
hover: new Style({
image: new Icon({
anchor: [88, 88],
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
scale: 0.25,
src: closedIconHover,
}),
}),
active: new Style({
image: new Icon({
anchor: [88, 88],
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
scale: 0.25,
src: closedIconActive,
}),
}),
};

// Event icon styles
export const eventStyles = {
incident: {
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/Components/events/EventCard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
}
}

&.major {
&.major, &.closure {
.event-card__details {
.type {
.header, .content {
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/src/Components/events/EventsTable.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ table {
&:nth-of-type(even) {
background-color: $Grey10;
}
&.major {
&.major, &.closure {
background-color: $Surface-status-red;

td.severity {
Expand All @@ -50,7 +50,7 @@ table {
text-transform: capitalize;
}



svg {
color: $BC-Blue;
Expand Down
5 changes: 4 additions & 1 deletion src/frontend/src/Components/map/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import { fromLonLat, transformExtent } from 'ol/proj';
import Overlay from 'ol/Overlay.js';

// Styling
import { eventStyles } from '../data/featureStyleDefinitions.js';
import { closureStyles, eventStyles } from '../data/featureStyleDefinitions.js';

// Static assets
export const getEventIcon = (event, state) => {
if (event.get('roadIsClosed')) {
return closureStyles[state];
}
const severity = event.get('severity').toLowerCase();
const display_category = event.get('display_category');
const geometry = event.getGeometry().getType();
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/Components/map/layers/eventsLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export function loadEventsLayers(

if (eventsData) {
// Initialize vss
const closureVS = createVS();
const closureLinesVS = createVS();
const majorEventsVS = createVS();
const majorEventsLinesVS = createVS();
const minorEventsVS = createVS();
Expand All @@ -35,13 +37,15 @@ export function loadEventsLayers(
const isLineSegment = feature.getGeometry().getType() === 'LineString';

const vsMap = {
closure: closureVS,
majorEvents: majorEventsVS,
minorEvents: minorEventsVS,
futureEvents: futureEventsVS,
roadConditions: roadConditionsVS
}

const lineVsMap = {
closure: closureLinesVS,
majorEvents: majorEventsLinesVS,
minorEvents: minorEventsLinesVS,
futureEvents: futureEventsLinesVS,
Expand Down Expand Up @@ -130,6 +134,8 @@ export function loadEventsLayers(
}

// Add layer to map for each vs
addLayer('closures', closureVS, 128);
addLayer('closuresLines', closureLinesVS, 2);
addLayer('majorEvents', majorEventsVS, 128);
addLayer('majorEventsLines', majorEventsLinesVS, 2);
addLayer('minorEvents', minorEventsVS, 128);
Expand Down
7 changes: 4 additions & 3 deletions src/frontend/src/Components/map/mapPopup.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import parse from 'html-react-parser';
import colocatedCamIcon from '../../images/colocated-camera.svg';

const displayCategoryMap = {
closure: 'Closure',
majorEvents: 'Major Delay',
minorEvents: 'Minor Delay',
futureEvents: 'Future Delay',
Expand Down Expand Up @@ -104,17 +105,17 @@ export function getEventPopup(eventFeature) {
<div className={`popup popup--delay ${severity}`}>
<div className="popup__title">
<p className="bold name">{`${eventData.route_at} - ${eventData.route_display}`}</p>
<p style={{'white-space': 'pre-wrap'}} className="bold orientation">{convertDirection(eventData.direction)}</p>
<p style={{'whiteSpace': 'pre-wrap'}} className="bold orientation">{convertDirection(eventData.direction)}</p>
</div>

<div className="popup__description">
<div className="delay-type">
<div className="bold delay-severity">
<div className="delay-icon">
<EventTypeIcon displayCategory={eventData.display_category} />
<EventTypeIcon displayCategory={ eventData.roadIsClosed ? 'closure' : eventData.display_category} />
</div>

<p className="bold">{displayCategoryMap[eventData.display_category]}</p>
<p className="bold">{ eventData.roadIsClosed ? 'Closure' : displayCategoryMap[eventData.display_category]}</p>
</div>

<p className="bold friendly-time--mobile">
Expand Down
Loading

0 comments on commit 057b86b

Please sign in to comment.