diff --git a/README.md b/README.md
index 4f1a3b7..48c8724 100644
--- a/README.md
+++ b/README.md
@@ -89,4 +89,4 @@ _Note: If you would prefer to keep your code challenge private, please share acc
| Xavier | https://github.com/xmedr |
| Hayley | https://github.com/haowens |
-Keep in mind that you cannot create a private fork of a public repository on GitHub, so you’ll need to [follow these instructions](https://gist.github.com/0xjac/85097472043b697ab57ba1b1c7530274) to create a private copy of the repo.
\ No newline at end of file
+Keep in mind that you cannot create a private fork of a public repository on GitHub, so you’ll need to [follow these instructions](https://gist.github.com/0xjac/85097472043b697ab57ba1b1c7530274) to create a private copy of the repo.
diff --git a/map/serializers.py b/map/serializers.py
index 03dd912..f3cd265 100644
--- a/map/serializers.py
+++ b/map/serializers.py
@@ -6,7 +6,7 @@
class CommunityAreaSerializer(serializers.ModelSerializer):
class Meta:
model = CommunityArea
- fields = ["name", "num_permits"]
+ fields = ["name", "area_id", "num_permits"]
num_permits = serializers.SerializerMethodField()
@@ -30,5 +30,13 @@ def get_num_permits(self, obj):
}
]
"""
+ year = self.context.get("year")
+ try:
+ year = int(year)
+ except (TypeError, ValueError):
+ return 0
- pass
+ return RestaurantPermit.objects.filter(
+ community_area_id=str(obj.area_id),
+ issue_date__year=year,
+ ).count()
\ No newline at end of file
diff --git a/map/static/js/RestaurantPermitMap.js b/map/static/js/RestaurantPermitMap.js
index 57f8ea0..984d65c 100644
--- a/map/static/js/RestaurantPermitMap.js
+++ b/map/static/js/RestaurantPermitMap.js
@@ -1,24 +1,24 @@
-import React, { useEffect, useState } from "react"
+import React, { useEffect, useState } from "react";
-import { MapContainer, TileLayer, GeoJSON } from "react-leaflet"
+import { MapContainer, TileLayer, GeoJSON } from "react-leaflet";
-import "leaflet/dist/leaflet.css"
+import "leaflet/dist/leaflet.css";
-import RAW_COMMUNITY_AREAS from "../../../data/raw/community-areas.geojson"
+import RAW_COMMUNITY_AREAS from "../../../data/raw/community-areas.geojson";
-function YearSelect({ setFilterVal }) {
+function YearSelect({ filterVal, setFilterVal }) {
// Filter by the permit issue year for each restaurant
- const startYear = 2026
+ const startYear = 2026;
const years = [...Array(11).keys()].map((increment) => {
- return startYear - increment
- })
+ return startYear - increment;
+ });
const options = years.map((year) => {
return (
- )
- })
+ );
+ });
return (
<>
@@ -28,70 +28,106 @@ function YearSelect({ setFilterVal }) {
>
- )
+ );
}
export default function RestaurantPermitMap() {
- const communityAreaColors = ["#eff3ff", "#bdd7e7", "#6baed6", "#2171b5"]
+ const communityAreaColors = ["#eff3ff", "#bdd7e7", "#6baed6", "#2171b5"];
- const [currentYearData, setCurrentYearData] = useState([])
- const [year, setYear] = useState(2026)
+ const [currentYearData, setCurrentYearData] = useState([]);
+ const [year, setYear] = useState(2026);
- const yearlyDataEndpoint = `/map-data/?year=${year}`
+ const yearlyDataEndpoint = `/map-data/?year=${year}`;
useEffect(() => {
- fetch()
+ fetch(yearlyDataEndpoint)
.then((res) => res.json())
.then((data) => {
- /**
- * TODO: Fetch the data needed to supply to map with data
- */
+ setCurrentYearData(data);
})
- }, [yearlyDataEndpoint])
+ .catch(() => {
+ setCurrentYearData([]);
+ });
+ }, [yearlyDataEndpoint]);
+ const totalPermits = currentYearData.reduce(
+ (sum, area) => sum + area.num_permits,
+ 0,
+ );
+
+ const maxNumPermits = currentYearData.reduce(
+ (maxPermits, area) => Math.max(maxPermits, area.num_permits),
+ 0,
+ );
+
+ const permitsByAreaId = currentYearData.reduce((lookup, area) => {
+ lookup[String(area.area_id)] = area.num_permits;
+ return lookup;
+ }, {});
function getColor(percentageOfPermits) {
- /**
- * TODO: Use this function in setAreaInteraction to set a community
- * area's color using the communityAreaColors constant above
- */
+ if (percentageOfPermits === 0) {
+ return communityAreaColors[0];
+ }
+ if (percentageOfPermits <= 0.05) {
+ return communityAreaColors[1];
+ }
+ if (percentageOfPermits <= 0.12) {
+ return communityAreaColors[2];
+ }
+
+ return communityAreaColors[3];
+ }
+
+ function toTitleCase(str) {
+ return str
+ .toLowerCase()
+ .split(" ")
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
+ .join(" ");
}
function setAreaInteraction(feature, layer) {
- /**
- * TODO: Use the methods below to:
- * 1) Shade each community area according to what percentage of
- * permits were issued there in the selected year
- * 2) On hover, display a popup with the community area's raw
- * permit count for the year
- */
- layer.setStyle()
- layer.on("", () => {
- layer.bindPopup("")
- layer.openPopup()
- })
+ const areaName = toTitleCase(feature.properties.community);
+ const areaId = feature.properties.area_numbe;
+ const permitCount = permitsByAreaId[String(areaId)] || 0;
+ const permitShare = totalPermits > 0 ? permitCount / totalPermits : 0;
+
+ layer.setStyle({
+ fillColor: getColor(permitShare),
+ fillOpacity: 0.7,
+ weight: 1,
+ opacity: 1,
+ });
+
+ layer.on("mouseover", () => {
+ layer.bindPopup(
+ `${areaName}
Restaurant permits issued in ${year}: ${permitCount}
Area share of citywide permits: ${(permitShare * 100).toFixed(1)}%`,
+ );
+ layer.openPopup();
+ });
+
+ layer.on("mouseout", () => {
+ layer.closePopup();
+ });
}
return (
<>
- Restaurant permits issued this year: {/* TODO: display this value */} + Restaurant permits issued this year: {totalPermits}
- Maximum number of restaurant permits in a single area: - {/* TODO: display this value */} + Maximum number of restaurant permits in a single area: {maxNumPermits}
-