auths
objectreturn
or this.key = 'value'
, pass input data to your code viaparams
, and maintain state across executions with$checkpoint.async
(event, steps) => {
}
if (!event.body.lat || !event.body.long) {
$end("Please pass both a lat and long to this workflow")
}
auths
objectreturn
or this.key = 'value'
, pass input data to your code viaparams
, and maintain state across executions with$checkpoint.async
(event, steps) => {
}
// To perform intersection queries with the SF Data API below,
// we need to create a box around the point where the recording was taken,
// and ask if any of our street segments (LineStrings) intersect with it
// We create a box with corners roughly 10 meters in every cardinal
// direction from the original point
const lat = parseFloat(event.body.lat)
const long = parseFloat(event.body.long)
const DISTANCE_IN_EACH_DIRECTION = .0002
// Simple math to generate box around the point (for San Francisco)
this.topLeftCorner = [lat + DISTANCE_IN_EACH_DIRECTION, long - DISTANCE_IN_EACH_DIRECTION]
this.topRightCorner = [lat + DISTANCE_IN_EACH_DIRECTION, long + DISTANCE_IN_EACH_DIRECTION]
this.bottomRightCorner = [lat - DISTANCE_IN_EACH_DIRECTION, long + DISTANCE_IN_EACH_DIRECTION]
this.bottomLeftCorner = [lat - DISTANCE_IN_EACH_DIRECTION, long - DISTANCE_IN_EACH_DIRECTION]
// Generate WKT (well-known text represenation of a geometry) POLYGON
// that corresponds to the box. WKT orders coordinates as (long, lat)
this.POLYGON = `POLYGON((${this.topLeftCorner[1]} ${this.topLeftCorner[0]},${this.topRightCorner[1]} ${this.topRightCorner[0]},${this.bottomRightCorner[1]} ${this.bottomRightCorner[0]},${this.bottomLeftCorner[1]} ${this.bottomLeftCorner[0]},${this.topLeftCorner[1]} ${this.topLeftCorner[0]}))`
auths
objectreturn
or this.key = 'value'
, pass input data to your code viaparams
, and maintain state across executions with$checkpoint.async
(event, steps, auths) => {
}
// Street Sweeping schedules, by street
// https://data.sfgov.org/City-Infrastructure/Street-Sweeping-Schedule/yhqp-riqs
this.intersectionString = `intersects(line, '${steps.calculate_box_around_point.POLYGON}')`
return await require("@pipedreamhq/platform").axios(this, {
url: `https://data.sfgov.org/resource/yhqp-riqs`,
headers: {
"X-App-Token": `${auths.san_francisco_open_data_datasf.app_token}`,
},
params: {
"$where": this.intersectionString
},
})
auths
objectreturn
or this.key = 'value'
, pass input data to your code viaparams
, and maintain state across executions with$checkpoint.async
(event, steps) => {
}
// For each street within our bounding box, generate a
// string in the format of our street cleaning signs:
//
// ${START_TIME} to ${END_TIME} ${WEEKDAY(S)}
//
// First, DataSF returns an entry per (street segment, side of street, cleaning day).
// Segments are referred to by Centerline Network Number. See
// https://data.sfgov.org/City-Infrastructure/Street-Sweeping-Schedule/yhqp-riqs .
// If a single side of a street is cleaned on Monday and Thursday, DataSF returns two
// entries. We need coalesce these entries into a single string.
//
// Moreover, times returned from DataSF are in 24-hour clock time,
// But the time on signs are in 12-hour time, so we convert.
const capitalize = require("lodash.capitalize")
const includes = require("lodash.includes")
const uniq = require('lodash.uniq')
this.cleaningTimes = {}
const matchingStreets = steps.get_streets_within_bounding_box.$return_value
if (!matchingStreets.length) {
$end("No streets returned from DataSF within bounding box")
}
for (const record of matchingStreets) {
const { cnn, cnnrightleft, fromhour, tohour, weekday } = record
// Street segment : side of street
const key = `${cnn}:${cnnrightleft}`
const cleaningTimeInstance = `${convert24HourTimeTo12HourTime(fromhour)} to ${convert24HourTimeTo12HourTime(tohour)}`
const weekdayLower = weekday.toLowerCase()
// If we've already stored the time portion of the string, add the day to the end
if (key in this.cleaningTimes) {
if (!(includes(this.cleaningTimes[key]["weekdays"], weekdayLower))) {
this.cleaningTimes[key]["weekdays"].push(weekdayLower)
this.cleaningTimes[key]["instances"].push({ fromhour, tohour, weekday })
}
continue
}
// Otherwise, add the time / date of this record
this.cleaningTimes[key] = {
schedule: cleaningTimeInstance,
weekdays: [weekdayLower],
instances: [{ fromhour, tohour, weekday }]
}
}
this.scheduleArray = []
this.instances = {}
for (const { schedule, weekdays, instances } of Object.values(this.cleaningTimes)) {
const uniqueWeekdays = uniq(weekdays.sort(compareWeekdays)).map(capitalize)
const scheduleString = `${schedule} ${uniqueWeekdays.join(", ")}`
if (includes(this.scheduleArray, scheduleString)) {
continue
}
this.scheduleArray.push(scheduleString)
this.instances[scheduleString] = instances
}
// HTTP RESPONSE
$respond({
body: {
schedules: this.scheduleArray,
instances: this.instances,
},
})
function convert24HourTimeTo12HourTime(stringHour) {
const hour = parseInt(stringHour)
if (hour >= 0 && hour <= 11) {
return `${hour}AM`
}
if (hour === 12) {
return `${hour}PM`
}
return `${hour - 12}PM`
}
function compareWeekdays(a, b) {
const weekdayNum = {
// Schedules contain abbreviated and full weekdays
'sun': 0,
'sunday': 0,
'mon': 1,
'monday': 1,
'tues': 2,
'tuesday': 2,
'wed': 3,
'wednesday': 3,
'thu': 4,
'thursday': 4,
'fri': 6,
'friday': 6,
'sat': 7,
'saturday': 7,
}
return weekdayNum[a.toLowerCase()] - weekdayNum[b.toLowerCase()]
}