Skip to content
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

Fixed issue with community area to tract mapping #3

Merged
merged 9 commits into from
Feb 26, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ Each of these files should define a single array containing zero or more "marker

### FAQs

- **How were the community and census tract Fusion tables created?** The City of Chicago Data Portal (HERE) provides this information in KML format (keyhole markup language, an XML schema used to represent geo-spacial data). Unfortunately, KML isn't particularly friendly for applications like ours. The metadata associated with each area (for example, the neighborhood name or census tract id) is stored in an HTML fragment that CDATA'd in the XML. In order to create a more useable representation, we wrote a small Groovy script to export the useful bits into CSV format, which was then uploaded to Fusion.
- How were the community and census tract Fusion tables created? The City of Chicago Data Portal (HERE) provides this information in KML format (keyhole markup language, an XML schema used to represent geo-spacial data). Unfortunately, KML isn't particularly friendly for applications like ours. The metadata associated with each area (for example, the neighborhood name or census tract id) is stored in an HTML fragment that CDATA'd in the XML. In order to create a more useable representation, we wrote a small Groovy script to export the useful bits into CSV format, which was then uploaded to Fusion.

- **Why is the boundary data in a Fusion Table?** It shouldn't be. There's no good reason for this other than legacy: When we started development the notion was that we'd use Fusion Tables as our primary database. We eventually migrated the other data away from Fusion, but haven't gotten around to moving the polygons out.
- Why is the boundary data in a Fusion Table? It shouldn't be. There's no good reason for this other than legacy: When we started development the notion was that we'd use Fusion Tables as our primary database. We eventually migrated the other data away from Fusion, but haven't gotten around to moving the polygons out.

- **How are the polygons shaded?**
- How are the polygons shaded?
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ <h4 class="vert-break">For calendar year</h4>
</div>
<div class="checkbox">
<input type="checkbox" id="relative-shading">
<label for="relative-shading" data-trigger="hover focus" data-container="body" data-toggle="popover" data-placement="right" data-content="When checked, census tracts and community areas will be shaded to show desertifaction relative to the areas currently visible on the map (rather than the city as a whole).">
<label for="relative-shading" data-trigger="hover focus" data-container="body" data-toggle="popover" data-placement="right" data-content="Shade areas relative only to others visible on the map (rather than the city as a whole).">
Relative shading
</label>
</div>
Expand Down
41 changes: 36 additions & 5 deletions public/js/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
(function (index, $) {

var multiselectData = undefined;

/* Initialize the area/geography type radio selection (census tracts vs. neighborhoods)
*/
function initGeoRadio() {
Expand All @@ -26,6 +28,7 @@

// Fetch license list from server and update the multiselect accordingly
json.fetch("licenses.json", function (data) {
multiselectData = data;
$("#business-multiselect").multiselect('dataprovider', data);
});

Expand Down Expand Up @@ -83,19 +86,47 @@
}

function getAreaType() {
return $('#neighborhood-radio').is(":checked") ? "neighborhood" : "census";
return $('#neighborhood-radio').is(":checked") ? "commareas" : "tracts";
}


function updateSliderRange () {
var selectedBusiness = getSelectedBusiness();
var min = 0;
var max = 0;

multiselectData.forEach(function (record) {
if (record.value == selectedBusiness) {
min = record["min-year"];
max = record["max-year"];
}
});

var selectedYear = $("#year-slider").slider("getValue");

$("#year-slider").slider("setAttribute", "min", min);
$("#year-slider").slider("setAttribute", "max", max);

if (selectedYear > max)
$("#year-slider").slider("setValue", max);

if (selectedYear < min)
$("#year-slider").slider("setValue", min);

$("#year-value").text($("#year-slider").slider("getValue"));
}

/*
* Invoked when a user makes a UI selection that affects map rendering
*/
index.update = function () {
var dataset = getAreaType() + "-" + getSelectedBusiness() + "-" + getSelectedYear() + ".json";
console.log(dataset);

updateSliderRange();

var dataset = getAreaType() + "/" + getSelectedBusiness() + "-" + getSelectedYear() + ".json";

// Update polygons and shading
// TODO: Cache this result and only fetch/update when required
if (getAreaType() == "census") {
if (getAreaType() == "tracts") {
maps.showCensusTracts();
maps.setCensusData(dataset);
} else {
Expand Down
102 changes: 54 additions & 48 deletions public/js/maps.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
var censusData = []; // current census desertification data

// When true, polygons are shaded relative only to other visible polygons
var relativeShadingEnabled = false;
var relativeShadingEnabled = false;

var map = null; // Google map object

Expand Down Expand Up @@ -170,7 +170,7 @@
}

/* Filter a given set of polygons returning an array containing only those currently visible
* on the map.
* on the map.
*/
function getVisiblePolygons(polys) {
var visiblePolys = [];
Expand All @@ -197,21 +197,19 @@

function getMaxIndex(polys, data) {
var max = 0;
polys.forEach(function (thisPoly) {
if (data[thisPoly.areaId] && data[thisPoly.areaId].ACCESS_INDEX > max) {
max = data[thisPoly.areaId].ACCESS_INDEX;
};
polys.forEach(function (poly) {
var index = getIndexForArea(poly.areaId, data);
if (index > max) max = index;
});

return max;
}

function getMinIndex(polys, data) {
var min = Number.MAX_VALUE;
polys.forEach(function (thisPoly) {
if (data[thisPoly.areaId] && data[thisPoly.areaId].ACCESS_INDEX < min) {
min = data[thisPoly.areaId].ACCESS_INDEX;
};
polys.forEach(function (poly) {
var index = getIndexForArea(poly.areaId, data);
if (index < min) min = index;
});

return min;
Expand All @@ -220,42 +218,67 @@
/* Re-shade visible polygons (may change opacity on when relative shading is enabled).
*/
function refreshPolygonShading() {

var activePolygons = (activeGeography == "census") ? censusPolys : communityPolys;
var activeDataset = (activeGeography == "census") ? censusData : communityData;

if (relativeShadingEnabled) {

// Blank polygons that are not visible
activePolygons.forEach(function (thisPoly) {
thisPoly.setOptions({
fillOpacity: 0
});
});

activePolygons = getVisiblePolygons(activePolygons);
}

shadePolygons(activePolygons, activeDataset);
}

function getIndexForArea(areaId, data) {
var areaProperty = (activeGeography == "census") ? "TRACT" : "COMMUNITY_AREA";
var index = undefined;

data.forEach(function (record) {
if (record[areaProperty] == areaId) {
index = record.ACCESS1;
}
});

return index;
}

function shadePolygons(polys, data) {

// Get min and max access index values for polygons
var max = getMaxIndex(polys, data);
var min = getMinIndex(polys, data);

// Blank polygons that are not visible
polys.forEach(function (thisPoly) {
thisPoly.setOptions({
fillOpacity: 0
});
});
var index = getIndexForArea(thisPoly.areaId, data);

// Shade visible polys relative to others
getVisiblePolygons(polys).forEach(function (thisPoly) {
var index = data[thisPoly.areaId] && data[thisPoly.areaId].ACCESS_INDEX;
thisPoly.setOptions({
fillOpacity: getOpacityBucket((index - min) / (max - min))
});
// No data available--color polygon in red
if (index == undefined) {
thisPoly.setOptions({
fillOpacity: 0.4,
fillColor: "#ff0000"
});
}

// Shade polygon based on bucket value
else {
thisPoly.setOptions({
fillOpacity: getOpacityBucket((index - min) / (max - min))
});
}
});
}

function getOpacityBucket(value) {
var bucketCount = 5;
return Math.round(value / (1 / bucketCount)) * (1 / bucketCount);
return 1 - Math.round(value / (1 / bucketCount)) * (1 / bucketCount);
}

function renderMarkers(places) {
Expand Down Expand Up @@ -339,45 +362,28 @@

maps.setRelativePolygonShading = function (isRelativeShadingEnabled) {
relativeShadingEnabled = isRelativeShadingEnabled;
refreshPolygonShading();
};

maps.setCommunityData = function (datafile) {

communityData = {};

// Generate fake data
communityPolys.forEach(function (thisPoly) {
communityData[thisPoly.areaId] = {
"ACCESS_INDEX": Math.random()
};
json.fetch(datafile, function (data) {
communityData = data;
shadePolygons(communityPolys, data);
});
shadePolygons(communityPolys, communityData);

// TODO: Uncomment to fetch real data
// json.fetch(datafile, function (data) {
// communityData = data;
// shadePolygons(communityPolys, data);
// });

activeGeography = "communities";
};

maps.setCensusData = function (datafile) {
censusData = {};

// Generate fake data
censusPolys.forEach(function (thisPoly) {
censusData[thisPoly.areaId] = {
"ACCESS_INDEX": Math.random()
};
json.fetch(datafile, function (data) {
censusData = data;
shadePolygons(censusPolys, data);
});
shadePolygons(censusPolys, censusData);

// TODO: Uncomment to fetch real data
// json.fetch(datafile, function (data) {
// censusData = data;
// shadePolygons(censusPolys, data);
// });

activeGeography = "census";
};
Expand Down
Loading