-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: WPRDC data fetching function #83
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,9 +32,6 @@ | |
//Array of markers | ||
var markers = new Array(); | ||
|
||
//WPRDC data | ||
const WPRDC_BASE_URL = 'https://data.wprdc.org/api/action/datastore_search_sql?sql='; | ||
|
||
//Create a new Date object for the current date | ||
var currentDate = new Date(); | ||
|
||
|
@@ -93,115 +90,166 @@ | |
//Listener for sidebar toggle | ||
document.getElementById("sidebarToggle").addEventListener("click", toggleSidebar); | ||
|
||
//City of Pittsburgh police data | ||
const CITY_POLICE_API = "1797ead8-8262-41cc-9099-cbc8a161924b"; | ||
const CITY_POLICE_SQL = `SELECT * from "${CITY_POLICE_API}" WHERE "INCIDENTNEIGHBORHOOD" LIKE '%Oakland'`; | ||
const CITY_POLICE_ICON = L.divIcon({ | ||
className: 'map-pin blue', | ||
html: '<i class="fa fa-balance-scale"></i>', | ||
iconSize: [32, 32], | ||
iconAnchor: [16, 32] | ||
}); // TODO: make a global dictionary for these? | ||
fetch(`${WPRDC_BASE_URL}${CITY_POLICE_SQL}`) | ||
// TODO: ensure 200 response | ||
.then((response) => response.json()) | ||
//TODO: should have some generic error handling for data | ||
.catch((err) => console.log(err)) | ||
.then((data) => { | ||
const records = data.result.records; | ||
records.forEach((record, i) => { | ||
//Collect time of incident from the record | ||
record.incidentYear = parseInt(record.INCIDENTTIME.substring(0, 4)); | ||
record.incidentMonth = parseInt(record.INCIDENTTIME.substring(5, 8)); | ||
record.incidentDay = parseInt(record.INCIDENTTIME.substring(8, 10)); | ||
|
||
record.pin = L.marker([record.Y, record.X], { icon: CITY_POLICE_ICON }); | ||
record.pin.addTo(map) | ||
.bindPopup(`${record.OFFENSES}`); | ||
|
||
//Push the marker and date to their respective arrays | ||
markers.push(record); | ||
}); | ||
}); | ||
//WPRDC data | ||
const WPRDC_BASE_URL = 'https://data.wprdc.org/api/action/datastore_search_sql?sql='; | ||
|
||
// Marker Icons | ||
const iconTypes = { | ||
CITY_POLICE: L.divIcon({ | ||
className: 'map-pin blue', | ||
html: '<i class="fa fa-balance-scale"></i>', | ||
iconSize: [32, 32], | ||
iconAnchor: [16, 32] | ||
}), | ||
CITY_311_ICON: L.divIcon({ | ||
className: 'map-pin yellow', | ||
html: '<i class="fa fa-commenting"></i>', | ||
iconSize: [32, 32], | ||
iconAnchor: [16, 32] | ||
}), | ||
LIBRARY_ICON: L.divIcon({ | ||
className: 'map-pin black', | ||
html: '<i class="fa fa-book"></i>', | ||
iconSize: [32, 32], | ||
iconAnchor: [16, 32] | ||
}) | ||
}; | ||
|
||
const WPRDC_DATA_SOURCES = { | ||
"Police": { | ||
id: '1797ead8-8262-41cc-9099-cbc8a161924b', | ||
primaryFiltering: 'WHERE "INCIDENTNEIGHBORHOOD" LIKE \'%Oakland\'', | ||
latLong: ['Y', 'X'], | ||
icon: iconTypes.CITY_POLICE, | ||
|
||
popup: (record) => record['OFFENSES'], | ||
|
||
processRecord: (record) => { | ||
// Collect time of incident from the record | ||
record.incidentYear = parseInt(record.INCIDENTTIME.substring(0,4)); | ||
record.incidentMonth = parseInt(record.INCIDENTTIME.substring(5,8)); | ||
record.incidentDay = parseInt(record.INCIDENTTIME.substring(8,10)); | ||
} | ||
}, | ||
|
||
// City of Pittsburgh 311 data | ||
// TODO: would be great to prune 311 data to the last 30 days, like the police data | ||
"311": { | ||
id: '40776043-ad00-40f5-9dc8-1fde865ff571', | ||
primaryFiltering: 'WHERE "NEIGHBORHOOD" LIKE \'%Oakland\' ORDER BY "CREATED_ON" DESC', | ||
latLong: ['Y', 'X'], | ||
icon: iconTypes.CITY_311_ICON, | ||
|
||
popup: (record) => record['REQUEST_TYPE'], | ||
|
||
processRecord: (record) => { | ||
// Collect time of incident from the record | ||
record.incidentYear = parseInt(record.CREATED_ON.substring(0,4)); | ||
record.incidentMonth = parseInt(record.CREATED_ON.substring(5,8)); | ||
record.incidentDay = parseInt(record.CREATED_ON.substring(8,10)); | ||
} | ||
}, | ||
|
||
// Calls from the library db | ||
"Library": { | ||
id: "2ba0788a-2f35-43aa-a47c-89c75f55cf9d", | ||
primaryFiltering: 'WHERE "Name" LIKE \'%OAKLAND%\'', | ||
latLong: ['Lat', 'Lon'], | ||
icon: iconTypes.LIBRARY_ICON, | ||
|
||
title: (record) => record['Name'], | ||
popup: (record) => ` | ||
<strong>${record.Name}</strong> | ||
<br> Address: ${record.Address} | ||
<br> Phone: ${record.Phone} | ||
<br> Monday: ${record.MoOpen.substring(0, 5)} - ${record.MoClose.substring(0, 5)} | ||
<br> Tuesday: ${record.TuOpen.substring(0, 5)} - ${record.TuClose.substring(0, 5)} | ||
<br> Wednesday: ${record.WeOpen.substring(0, 5)} - ${record.WeClose.substring(0, 5)} | ||
<br> Thursday: ${record.ThOpen.substring(0, 5)} - ${record.ThClose.substring(0, 5)} | ||
<br> Friday: ${record.FrOpen.substring(0, 5)} - ${record.FrClose.substring(0, 5)} | ||
<br> Saturday: ${record.SaOpen.substring(0, 5)} - ${record.SaClose.substring(0, 5)} | ||
<br> Sunday: ${record.SuOpen.substring(0, 5)} - ${record.SuClose.substring(0, 5)} | ||
` | ||
} | ||
}; | ||
|
||
const WPRDC_QUERY_PREFIX = 'SELECT * FROM "'; | ||
const WPRDC_QUERY_SUFFIX = '" '; | ||
|
||
// Fetch data from West Pennsylvania Regional Data Center using the SQL API | ||
function fetchWPRDCData(dataSourceName, options) { | ||
if (!options) { | ||
options = {}; | ||
} | ||
|
||
console.group(`${dataSourceName} API`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prune out all logging |
||
|
||
const dataSource = WPRDC_DATA_SOURCES[dataSourceName]; | ||
let query = WPRDC_QUERY_PREFIX + dataSource.id + WPRDC_QUERY_SUFFIX + dataSource.primaryFiltering; | ||
|
||
if (options.limit) { | ||
console.log(`Limit set to: ${options.limit}`); | ||
query += ' LIMIT ' + options.limit; | ||
} | ||
|
||
console.log(`Final query: ${query}`); | ||
console.groupEnd(); | ||
|
||
return fetch(WPRDC_BASE_URL + query) | ||
// TODO: ensure 200 response | ||
.then((response) => response.json()) | ||
// TODO: should have some generic error handling for data | ||
.catch((err) => console.log(err)) | ||
.then((data) => { | ||
const records = data.result.records; | ||
|
||
records.forEach((record, i) => { | ||
// TODO: Check browser compatability for `instanceof Function` | ||
if (dataSource.processRecord instanceof Function) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can do without the |
||
dataSource.processRecord(record, i); | ||
} | ||
|
||
const latLong = dataSource.latLong.map((fieldName) => record[fieldName]); | ||
const latLongNoNulls = latLong.some((field) => !!field); | ||
if (latLongNoNulls) { | ||
let title = null; | ||
if (dataSource.title instanceof Function) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's assume that all datasets have a title and that it is a function.In fact, could you add this for the police and 311? It can be the field that you're using now for the display. |
||
title = dataSource.title(record); | ||
} | ||
|
||
record.pin = L.marker(latLong, { | ||
title: title, | ||
icon: dataSource.icon | ||
}); | ||
|
||
if (dataSource.popup instanceof Function) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, with #79 we can assume that all datasets will have a function to generate the popup that can , so we don't have to have this check, I think. |
||
record.pin.bindPopup(dataSource.popup(record)); | ||
} else { | ||
record.pin.bindPopup(record[dataSource.popup]) | ||
} | ||
|
||
record.pin.addTo(map) | ||
markers.push(record); | ||
} else { | ||
console.log('Found a null location!', record); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll fix this with #61 |
||
} | ||
}) | ||
}); | ||
} | ||
|
||
//TODO: would be great to prune 311 data to the last 30 days, like the police data | ||
//City of Pittsburgh 311 data | ||
const CITY_311_API = "40776043-ad00-40f5-9dc8-1fde865ff571"; | ||
const CITY_311_SQL = `SELECT * FROM "${CITY_311_API}" WHERE "NEIGHBORHOOD" LIKE '%Oakland' ORDER BY "CREATED_ON" DESC LIMIT 25`; | ||
const CITY_311_ICON = L.divIcon({ | ||
className: 'map-pin yellow', | ||
html: '<i class="fa fa-commenting"></i>', | ||
iconSize: [32, 32], | ||
iconAnchor: [16, 32] | ||
Promise.all([ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, this looks so clean |
||
fetchWPRDCData('Police', { limit: 250 }), | ||
fetchWPRDCData('311', { limit: 250 }), | ||
fetchWPRDCData('Library') | ||
]).then(() => { | ||
console.log('All data loaded'); | ||
}).catch((err) => { | ||
console.log('error fetching data', err); | ||
}); | ||
fetch(`${WPRDC_BASE_URL}${CITY_311_SQL}`) | ||
// TODO: ensure 200 response | ||
.then((response) => response.json()) | ||
//TODO: should have some generic error handling for data | ||
.catch((err) => console.log(err)) | ||
.then((data) => { | ||
const records = data.result.records; | ||
records.forEach((record, i) => { | ||
|
||
//Collect time of incident from the record | ||
record.incidentYear = parseInt(record.CREATED_ON.substring(0, 4)); | ||
record.incidentMonth = parseInt(record.CREATED_ON.substring(5, 8)); | ||
record.incidentDay = parseInt(record.CREATED_ON.substring(8, 10)); | ||
|
||
record.pin = L.marker([record.Y, record.X], { | ||
icon: CITY_311_ICON, | ||
title: record.REQUEST_TYPE || 'default title', | ||
zIndexOffset: 100 | ||
}); | ||
record.pin.bindPopup(`<pre>${JSON.stringify(record, null, 2)}</pre>`); | ||
record.pin.addTo(map); | ||
|
||
//Push the marker and date to their respective arrays | ||
markers.push(record); | ||
}); | ||
}); | ||
|
||
//Helper function that returns difference between two dates in days | ||
function getDateDifference(dateA, dateB) { | ||
return Math.floor(Math.abs(dateA.getTime() - dateB.getTime()) / 86400000); | ||
} | ||
//Calls from the library db | ||
const LibraryAPI = "2ba0788a-2f35-43aa-a47c-89c75f55cf9d"; | ||
const Library_SQL = `SELECT * FROM "${LibraryAPI}" WHERE "Name" LIKE '%OAKLAND%'`; | ||
const Library_ICON = L.divIcon({ | ||
className: 'map-pin black', | ||
html: '<i class="fa fa-book"></i>', | ||
iconSize: [32, 32], | ||
iconAnchor: [16, 32] | ||
}); | ||
fetch(`${WPRDC_BASE_URL}${Library_SQL}`) | ||
.then((response) => response.json()) | ||
.catch((err) => console.log(err)) | ||
.then((data) => { | ||
const libRecords = data.result.records; | ||
libRecords.forEach((record, i) => { | ||
//Library Icon from their twitter | ||
record.pin = L.marker([record.Lat, record.Lon], { | ||
icon: Library_ICON, | ||
title: record.Name, | ||
zIndexOffset: 100 | ||
}); | ||
// Probably a better way to format the library popup but its cleaner for now | ||
record.pin.bindPopup(` | ||
<strong>${record.Name}</strong> | ||
<br> Address: ${record.Address} | ||
<br> Phone: ${record.Phone} | ||
<br> Monday: ${record.MoOpen.substring(0, 5)} - ${record.MoClose.substring(0, 5)} | ||
<br> Tuesday: ${record.TuOpen.substring(0, 5)} - ${record.TuClose.substring(0, 5)} | ||
<br> Wednesday: ${record.WeOpen.substring(0, 5)} - ${record.WeClose.substring(0, 5)} | ||
<br> Thursday: ${record.ThOpen.substring(0, 5)} - ${record.ThClose.substring(0, 5)} | ||
<br> Friday: ${record.FrOpen.substring(0, 5)} - ${record.FrClose.substring(0, 5)} | ||
<br> Saturday: ${record.SaOpen.substring(0, 5)} - ${record.SaClose.substring(0, 5)} | ||
<br> Sunday: ${record.SuOpen.substring(0, 5)} - ${record.SuClose.substring(0, 5)} | ||
`); | ||
record.pin.addTo(map); | ||
markers.push(record); | ||
}); | ||
}); | ||
|
||
})(typeof window !== "undefined" ? window : {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, we can set default JS params in ES6, like:
function fetchWPRDCData(dataSourceName, options={}) {
So we don't have to have the conditional after. We're being selective in what es6 features we use, so this is fine as is. Just FYI.