Skip to content

Commit f2fffbc

Browse files
authored
feat: (urban tool) add support for aoi and region url params (#262)
# Description Want to be able iframe with zoom to US/CONUS/States/Urban region. Adding support for that via url params.
2 parents e8e8885 + f275dea commit f2fffbc

File tree

6 files changed

+364
-61
lines changed

6 files changed

+364
-61
lines changed

urban-dashboard/src/components/dashboard/index.jsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,48 @@ import Box from '@mui/material/Box';
44
import { InfoSidebar } from '../infoSidebar';
55
import MapBoxViewerWrapper from '../mapboxViewer/MapboxViewerWrapper';
66

7-
export function Dashboard({ dataset, urbanRegions, zoomLevel, zoomLocation }) {
8-
const [urbanRegion, setUrbanRegion] = useState("");
7+
export function Dashboard({
8+
dataset,
9+
urbanRegions,
10+
zoomLevel,
11+
zoomLocation,
12+
selectedAoi,
13+
selectedUrbanRegion,
14+
updateURLParams
15+
}) {
16+
const [urbanRegion, setUrbanRegion] = useState(selectedUrbanRegion);
917
const [zoomOut, setZoomOut] = useState(false);
1018
const handleZoomOut = () => {
1119
setZoomOut(!zoomOut);
1220
};
1321

22+
const handleUrbanRegionSelection = (region) => {
23+
updateURLParams({
24+
region: region
25+
});
26+
setUrbanRegion(region)
27+
}
28+
1429
return (
1530
<Box
1631
className="fullSize">
1732
<div className="dashboard-container">
1833
<InfoSidebar
1934
urbanRegions={urbanRegions}
2035
selection={urbanRegion}
21-
setSelection={setUrbanRegion}
36+
setSelection={handleUrbanRegionSelection}
2237
handleZoomOut={handleZoomOut}
2338
dataset={dataset}
2439
/>
2540
<MapBoxViewerWrapper
2641
urbanRegions={urbanRegions}
2742
urbanRegion={urbanRegion}
28-
setSelection={setUrbanRegion}
43+
setSelection={handleUrbanRegionSelection}
2944
zoomOut={zoomOut}
3045
dataset={dataset}
3146
zoomLevel={zoomLevel}
3247
zoomLocation={zoomLocation}
48+
selectedAoi={selectedAoi}
3349
/>
3450
</div>
3551
</Box>

urban-dashboard/src/components/dashboardContainer/index.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ export function DashboardContainer({
1010
defaultZoomLevel,
1111
defaultZoomLocation,
1212
defaultDataset,
13+
selectedAoi,
14+
selectedUrbanRegion,
15+
updateURLParams
1316
}) {
1417
const { config } = useConfig();
1518
const [dataset] = useState(defaultDataset || "gra2pes"); //vulcan, gra2pes (default)
@@ -45,6 +48,9 @@ export function DashboardContainer({
4548
zoomLocation={zoomLocation}
4649
dataset={dataset}
4750
urbanRegions={urbanRegions}
51+
selectedAoi={selectedAoi}
52+
selectedUrbanRegion={selectedUrbanRegion}
53+
updateURLParams={updateURLParams}
4854
/>
4955
)}
5056
</>

urban-dashboard/src/components/mapboxViewer/MapboxViewer.jsx

Lines changed: 112 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@ import mapboxgl from "mapbox-gl";
33
import "mapbox-gl/dist/mapbox-gl.css";
44
import Grid from "@mui/material/Grid";
55
import Box from "@mui/material/Box";
6-
import { VULCAN_RASTER_URL, GRA2PES_RASTER_URL } from "./helper";
6+
import { VULCAN_RASTER_URL, GRA2PES_RASTER_URL, AOI_BOUNDS } from "./helper";
77
import "./index.css";
8-
import defaultZoomLocation from '../../App'
8+
9+
910
export class MapBoxViewer extends Component {
1011
constructor(props) {
1112
super(props);
1213
this.state = {
1314
currentViewer: null,
14-
selectedUrbanRegion: null,
15+
mapLoaded: false,
16+
selectedUrbanRegion: props.urbanRegion
1517
};
1618
}
1719

@@ -31,13 +33,15 @@ export class MapBoxViewer extends Component {
3133
touchZoomRotate: false,
3234
});
3335

34-
this.setState({ currentViewer: map });
36+
this.setState({ currentViewer: map, mapLoaded: false });
3537

3638
// map.addControl(HomeButtonControl);
3739
map.addControl(new mapboxgl.NavigationControl());
3840

3941
// add the tile sources
4042
map.on("load", () => {
43+
this.setState({ mapLoaded: true });
44+
4145
map.addSource("raster-tiles-vulcan", {
4246
type: "raster",
4347
tiles: [VULCAN_RASTER_URL],
@@ -69,11 +73,33 @@ export class MapBoxViewer extends Component {
6973
}
7074

7175
// Move label layers above raster layers
72-
map.moveLayer("country-label");
73-
map.moveLayer("state-label");
74-
map.moveLayer("settlement-major-label");
75-
map.moveLayer("settlement-minor-label");
76-
map.moveLayer("admin-1-boundary");
76+
const labelLayers = [
77+
'country-label',
78+
'state-label',
79+
'settlement-major-label',
80+
'settlement-minor-label',
81+
'admin-1-boundary'
82+
];
83+
labelLayers.forEach(id => {
84+
if (map.getLayer(id)) map.moveLayer(id);
85+
});
86+
87+
// Handle initial urban region selection or AOI bounds
88+
if (this.props.urbanRegion) {
89+
const selectedRegion = this.props.urbanRegions.find(
90+
item => item.name === this.props.urbanRegion
91+
);
92+
if (selectedRegion) {
93+
this.handleUrbanRegionSelection(
94+
map,
95+
this.props.urbanRegion,
96+
selectedRegion.center,
97+
selectedRegion.geojson
98+
);
99+
}
100+
} else {
101+
this.applyAOIBounds(map);
102+
}
77103
});
78104
// Catch general map errors (like tile decoding failure)
79105
map.on("error", (e) => {
@@ -106,6 +132,10 @@ export class MapBoxViewer extends Component {
106132

107133
// Trigger zoom out when zoomOut button is clicked.
108134
componentDidUpdate = (prevProps) => {
135+
const { currentViewer, mapLoaded } = this.state;
136+
137+
if (!mapLoaded || !currentViewer) return; // Guard until map is ready
138+
109139
if (prevProps.zoomOut !== this.props.zoomOut) {
110140
this.resetMapView();
111141
}
@@ -115,54 +145,60 @@ export class MapBoxViewer extends Component {
115145
}
116146

117147
if (prevProps.urbanRegion !== this.props.urbanRegion) {
118-
const urbanRegion = this.props.urbanRegions.filter(
119-
(item) => item.name === this.props.urbanRegion
120-
)[0];
121-
if (urbanRegion) {
122-
// const name = urbanRegion.center;
123-
const center = urbanRegion.center;
124-
const geojson = urbanRegion.geojson;
125-
126-
// update selected region
127-
// this.setState({ selectedUrbanRegion: name });
128-
// this.props.setSelection(name);
129-
130-
//focus on selected region
131-
this.focusSelectedUrbanRegion(
132-
this.state.currentViewer,
133-
center,
134-
geojson
148+
console.log('urban region changed to', this.props.urbanRegion);
149+
150+
const selectedRegion = this.props.urbanRegions.find(
151+
item => item.name === this.props.urbanRegion
152+
);
153+
if (selectedRegion) {
154+
console.log('selected region is:', selectedRegion);
155+
this.handleUrbanRegionSelection(
156+
currentViewer,
157+
this.props.urbanRegion,
158+
selectedRegion.center,
159+
selectedRegion.geojson
135160
);
136161
}
137162
}
163+
164+
if (prevProps.selectedAoi !== this.props.selectedAoi && !this.props.urbanRegion) {
165+
this.applyAOIBounds(currentViewer);
166+
}
138167
};
139168

140169
resetMapView = () => {
141170
const { currentViewer } = this.state;
142-
if (currentViewer) {
143-
this.props.setSelection("");
144-
this.setState({
145-
selectedUrbanRegion: false, //giving this incorrect state will force it to reset
146-
});
147171

148-
// Zoom out and fly back to center and remove all the geoJSON layers
149-
const currentMap = this.state.currentViewer;
150-
currentMap.flyTo({
151-
center: [-98.771556, 32.967243],
152-
zoom: 4,
153-
speed: 1.2,
154-
curve: 1.42,
155-
});
156-
157-
if (currentMap.getLayer("boundary-fill"))
158-
currentMap.removeLayer("boundary-fill");
159-
if (currentMap.getLayer("boundary-outline"))
160-
currentMap.removeLayer("boundary-outline");
161-
if (currentMap.getSource("urban-boundary"))
162-
currentMap.removeSource("urban-boundary");
163-
} else {
164-
console.log("Map instance not initialized yet...");
172+
if (!currentViewer) {
173+
console.log('Map instance not initialized yet...');
174+
return;
165175
}
176+
177+
this.props.setSelection("");
178+
this.setState({
179+
selectedUrbanRegion: false, //giving this incorrect state will force it to reset
180+
});
181+
182+
// Zoom out and fly back to center and remove all the geoJSON layers
183+
currentMap.flyTo({
184+
center: [-98.771556, 32.967243],
185+
zoom: 4,
186+
speed: 1.2,
187+
curve: 1.42,
188+
});
189+
190+
if (currentMap.getLayer("boundary-fill"))
191+
currentMap.removeLayer("boundary-fill");
192+
if (currentMap.getLayer("boundary-outline"))
193+
currentMap.removeLayer("boundary-outline");
194+
if (currentMap.getSource("urban-boundary"))
195+
currentMap.removeSource("urban-boundary");
196+
};
197+
198+
handleUrbanRegionSelection = (map, name, center, geojson) => {
199+
this.setState({ selectedUrbanRegion: name });
200+
this.props.setSelection(name);
201+
this.focusSelectedUrbanRegion(map, center, geojson);
166202
};
167203

168204
plotUrbanRegions = (map, urbanRegions) => {
@@ -172,30 +208,27 @@ export class MapBoxViewer extends Component {
172208
const el = document.createElement("div");
173209
el.className = "marker";
174210

175-
let marker = this.addMarker(map, el, name, lon, lat);
211+
const marker = this.addMarker(map, el, name, lon, lat);
176212

177213
// when clicked on a urban region, focus on it
178214
marker.getElement().addEventListener("click", () => {
179-
this.setState({ selectedUrbanRegion: name });
180-
this.props.setSelection(name);
181-
this.focusSelectedUrbanRegion(map, center, geojson);
215+
this.handleUrbanRegionSelection(map, name, center, geojson);
182216
});
183217
});
184218
};
185219

186220
addMarker = (map, element, name, lon, lat) => {
187-
let marker = new mapboxgl.Marker(element)
221+
const marker = new mapboxgl.Marker(element)
188222
.setLngLat([lon, lat])
189-
// .setPopup(new mapboxgl.Popup({ offset: 25 })
190-
// .setText(name)
191223
.addTo(map);
192224

193-
const tooltipContent = `<strong>${name}<strong>`;
225+
const tooltipContent = `<strong>${name}</strong>`;
194226
const popup = new mapboxgl.Popup({
195227
closeButton: false,
196228
offset: [-3, -15],
197229
anchor: "bottom",
198230
}).setHTML(tooltipContent);
231+
199232
marker.setPopup(popup);
200233
marker.getElement().addEventListener("mouseenter", () => {
201234
popup.addTo(map);
@@ -251,6 +284,29 @@ export class MapBoxViewer extends Component {
251284
});
252285
};
253286

287+
applyAOIBounds = map => {
288+
const { selectedAoi : aoi } = this.props;
289+
290+
if (!aoi || !AOI_BOUNDS[aoi]) {
291+
console.log('No valid AOI specified or AOI not found in bounds');
292+
return;
293+
}
294+
295+
const bounds = AOI_BOUNDS[aoi];
296+
const bbox = [
297+
bounds.southwest[0],
298+
bounds.southwest[1],
299+
bounds.northeast[0],
300+
bounds.northeast[1]
301+
];
302+
303+
console.log(`Applying AOI bounds for ${aoi}:`, bbox);
304+
305+
map.fitBounds(bbox, {
306+
padding: 100
307+
});
308+
};
309+
254310
render() {
255311
return (
256312
<Box component="main" className="map-section">

urban-dashboard/src/components/mapboxViewer/helper/index.jsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,66 @@ export const BASEMAP_ID_DEFAULT = 'satellite';
3232
// export const GRA2PES_RASTER_URL = "https://a.tiles.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.jpg90?access_token=pk.eyJ1IjoiY292aWQtbmFzYSIsImEiOiJjbGNxaWdqdXEwNjJnM3VuNDFjM243emlsIn0.NLbvgae00NUD5K64CD6ZyA";
3333
export const VULCAN_RASTER_URL = "https://earth.gov/ghgcenter/api/raster/searches/dc3da6e6c0b1a2e941713c1c5932cb84/tiles/WebMercatorQuad/{z}/{x}/{y}?assets=total-co2&colormap_name=spectral_r&rescale=0%2C4000"
3434
export const GRA2PES_RASTER_URL = "https://earth.gov/ghgcenter/api/raster/searches/50ad78abf9e9c642971cdce22ef8baf0/tiles/WebMercatorQuad/{z}/{x}/{y}?assets=co2&colormap_name=spectral_r&rescale=0%2C2000"
35+
36+
37+
// Define bounds for different areas of interest
38+
export const AOI_BOUNDS = {
39+
CONUS: {
40+
southwest: [-125.0, 24.0], // [lng, lat]
41+
northeast: [-66.0, 49.0],
42+
},
43+
US: {
44+
southwest: [-180.0, 18.0], // [lng, lat] - includes Hawaii and Alaska
45+
northeast: [-66.0, 72.0],
46+
},
47+
Alabama: { southwest: [-88.5, 30.2], northeast: [-84.9, 35.0] },
48+
Alaska: { southwest: [-179.1, 51.2], northeast: [179.9, 71.5] },
49+
Arizona: { southwest: [-114.8, 31.3], northeast: [-109.0, 37.0] },
50+
Arkansas: { southwest: [-94.6, 33.0], northeast: [-89.6, 36.5] },
51+
California: { southwest: [-124.5, 32.5], northeast: [-114.0, 42.0] },
52+
Colorado: { southwest: [-109.1, 36.9], northeast: [-102.0, 41.0] },
53+
Connecticut: { southwest: [-73.7, 40.9], northeast: [-71.8, 42.0] },
54+
Delaware: { southwest: [-75.8, 38.5], northeast: [-75.0, 39.8] },
55+
Florida: { southwest: [-87.6, 24.4], northeast: [-80.0, 31.0] },
56+
Georgia: { southwest: [-85.6, 30.3], northeast: [-80.8, 35.0] },
57+
Hawaii: { southwest: [-160.3, 18.9], northeast: [-154.8, 22.2] },
58+
Idaho: { southwest: [-117.2, 42.0], northeast: [-111.0, 49.0] },
59+
Illinois: { southwest: [-91.5, 36.9], northeast: [-87.0, 42.5] },
60+
Indiana: { southwest: [-88.1, 37.7], northeast: [-84.8, 41.8] },
61+
Iowa: { southwest: [-96.6, 40.4], northeast: [-90.1, 43.5] },
62+
Kansas: { southwest: [-102.1, 36.9], northeast: [-94.6, 40.0] },
63+
Kentucky: { southwest: [-89.6, 36.5], northeast: [-81.9, 39.1] },
64+
Louisiana: { southwest: [-94.0, 28.9], northeast: [-88.8, 33.0] },
65+
Maine: { southwest: [-71.1, 43.0], northeast: [-66.9, 47.5] },
66+
Maryland: { southwest: [-79.5, 37.9], northeast: [-75.0, 39.7] },
67+
Massachusetts: { southwest: [-73.5, 41.2], northeast: [-69.9, 42.9] },
68+
Michigan: { southwest: [-90.4, 41.7], northeast: [-82.4, 48.3] },
69+
Minnesota: { southwest: [-97.3, 43.5], northeast: [-89.5, 49.4] },
70+
Mississippi: { southwest: [-91.7, 30.2], northeast: [-88.1, 34.9] },
71+
Missouri: { southwest: [-95.8, 35.9], northeast: [-89.0, 40.6] },
72+
Montana: { southwest: [-116.1, 44.4], northeast: [-104.0, 49.0] },
73+
Nebraska: { southwest: [-104.1, 40.0], northeast: [-95.3, 43.0] },
74+
Nevada: { southwest: [-120.0, 35.0], northeast: [-114.0, 42.0] },
75+
'New Hampshire': { southwest: [-72.6, 42.7], northeast: [-70.6, 45.3] },
76+
'New Jersey': { southwest: [-75.6, 38.9], northeast: [-73.9, 41.4] },
77+
'New Mexico': { southwest: [-109.1, 31.3], northeast: [-103.0, 37.0] },
78+
'New York': { southwest: [-79.8, 40.5], northeast: [-71.8, 45.0] },
79+
'North Carolina': { southwest: [-84.3, 33.8], northeast: [-75.5, 36.6] },
80+
'North Dakota': { southwest: [-104.0, 45.9], northeast: [-96.5, 49.0] },
81+
Ohio: { southwest: [-84.8, 38.4], northeast: [-80.5, 41.9] },
82+
Oklahoma: { southwest: [-103.0, 33.6], northeast: [-94.4, 37.0] },
83+
Oregon: { southwest: [-124.6, 41.9], northeast: [-116.5, 46.3] },
84+
Pennsylvania: { southwest: [-80.5, 39.7], northeast: [-74.7, 42.3] },
85+
'Rhode Island': { southwest: [-71.9, 41.1], northeast: [-71.1, 42.0] },
86+
'South Carolina': { southwest: [-83.3, 32.0], northeast: [-78.5, 35.2] },
87+
'South Dakota': { southwest: [-104.1, 42.5], northeast: [-96.4, 45.9] },
88+
Tennessee: { southwest: [-90.3, 34.9], northeast: [-81.6, 36.7] },
89+
Texas: { southwest: [-106.6, 25.8], northeast: [-93.5, 36.5] },
90+
Utah: { southwest: [-114.1, 36.9], northeast: [-109.0, 42.0] },
91+
Vermont: { southwest: [-73.4, 42.7], northeast: [-71.5, 45.0] },
92+
Virginia: { southwest: [-83.7, 36.5], northeast: [-75.2, 39.5] },
93+
Washington: { southwest: [-124.8, 45.5], northeast: [-116.9, 49.0] },
94+
'West Virginia': { southwest: [-82.6, 37.2], northeast: [-77.7, 40.6] },
95+
Wisconsin: { southwest: [-92.9, 42.5], northeast: [-86.7, 47.3] },
96+
Wyoming: { southwest: [-111.1, 40.9], northeast: [-104.1, 45.0] },
97+
};

0 commit comments

Comments
 (0)