L = {
const L = await require("leaflet/dist/leaflet.js");
if (!L._style) {
const href = await require.resolve("leaflet/dist/leaflet.css");
document.head.appendChild(L._style = html`<link href=${href} rel=stylesheet>`);
}
L.esri = await require("esri-leaflet/dist/esri-leaflet.js");
// L.esri.Heat = await r("esri-leaflet-heatmap");
// await r("leaflet.heat").catch(() => L.heatLayer);
return L;
};
I am an avid birder and some of my favorite places to bird include local, state, and federal public lands. Why public lands? Because they are (usually) open to the public, and provide unique opportunities to see some amazing wildlife (including birds)! Below you can select and view some of the public lands I’ve visited. You can even see the most recent bird sightings (last 2 weeks for up to 10 selected public lands), but you’ll need to enter your eBird API key and hit the Run
button. If you don’t have an eBird API key, then visit this page.
fed_public_lands = await FileAttachment("data/protected_lands_visited.geojson").json();
// load json
eBird_hotspots = await FileAttachment("data/protected_lands_eBird.json").json();
land_names = {
let a = fed_public_lands.features.map(x => x.properties.Unit_Nm);
// get unique values
return [... new Set(a)];
};
call_eBird = function() {
let txt = "";
let url = "https://api.ebird.org/v2/data/obs/US/recent?back=14&hotspot=true&r=";
if (results.length > 0) {
// txt = txt + url + results.map(e => eBird_hotspots.hotspot_id[eBird_hotspots.fed_land.indexOf(e)])
txt = txt + url + results.map(e => eBird_hotspots[e])
return fetch(txt, {
headers: {
'x-ebirdapitoken': eBird_api_key,
}
})
.then(res => {
let bird_sightings = res.json();
// console.log(bird_sightings);
return bird_sightings;
});
};
};
buildResultsContainer = {
d3.select(".results-container")
.append("div")
.attr("class", "results")
.append("p");
d3.select(".results-container")
.insert("label", ".results")
.text("Public Land(s) (selected):");
};
container = {
let x = d3.create("div")
// x.attr("style", `width:100%;height:${window.outerHeight * 0.65}px`);
x.attr("style", `width:100%; min-height:750px`);
return x.node();
}
public_lands = {
// let map = L.map(container, {minZoom: 0, maxZoom: 16}); // define zoom level globally, but decided to do this on a layer by layer basis
// https://developers.arcgis.com/documentation/mapping-apis-and-services/reference/zoom-levels-and-scale/#conversion-tool
// LOD is the zoom level, if not specified use the conversion tool to translate min / max scale to zoom level
let map = L.map(container);
// add basemap layers
const usgs = "<a href='https://www.usgs.gov/'>U.S. Geological Survey</a> | <a href='https://www.usgs.gov/laws/policies_notices.html'>Policies</a>";
const OSM_Basemap = L.tileLayer(
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
{
attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a>",
ext: 'png',
minZoom: 0,
maxZoom: 19
});
const MassGIS_Basemap = L.esri.tiledMapLayer({
url: 'https://tiles.arcgis.com/tiles/hGdibHYSPO59RG1h/arcgis/rest/services/MassGISBasemap/MapServer',
attribution: "<a href='https://massgis.maps.arcgis.com/home/user.html?user=MassGIS'>MassGIS, Mass. Executive Office of Technology Services and Security</a>",
ext: 'png',
minZoom: 7,
maxZoom: 19
});
const USGS_Img = L.tileLayer(
'https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}',
{
attribution: usgs,
ext: 'png',
minZoom: 0,
maxZoom: 16
});
const USGS_Topo = L.tileLayer(
'https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}',
{
attribution: usgs,
ext: 'png',
minZoom: 0,
maxZoom: 16
}).addTo(map);
// button to toggle basemap layer selection
L.control.layers(
{
"USGS Topo": USGS_Topo,
"USGS Imagery Topo": USGS_Img,
"MassGIS": MassGIS_Basemap,
"OSM": OSM_Basemap
},
null,
{position: 'topleft'}
).addTo(map);
L.control.scale().addTo(map);
map.setView([39.0, -77.0], 6);
return map;
};
styles = ({
basic: {
weight: 0.8,
fillColor: "#696969",
fillOpacity: 0.4,
color: "#5e5e5e",
opacity: 0.6
},
highlight: {
weight: 1.2,
fillColor: "#35e6ab",
fillOpacity: 0.6,
color: "#5e5e5e",
opacity: 0.8
},
select: {
weight: 1.0,
fillColor: "#e69035",
fillOpacity: 0.4,
color: "#5e5e5e",
opacity: 0.6
},
select_co: {
weight: 5.0,
fillColor: "#e69035",
fillOpacity: 1.0,
color: "#e69035",
opacity: 1.0
}
});
styles = Object {basic: Object, highlight: Object, select: Object, select_co: Object}
mutable results = Mutable {}
results = Array(0) []
geojson = {
// indexOf returns index number if e exists, -1 otherwise
const idx = (e) => { return results.indexOf(e.feature.properties.Unit_Nm); };
const highlightFeature = (e) => {
e.target.setStyle(styles.highlight);
e.target.bringToFront();
e.target.openPopup();
};
const resetHighlight = (e) => {
let q = (idx(e.target) < 0) ? styles.basic : styles.select;
e.target.setStyle(q);
e.target.closePopup();
};
const toggleFeature = (e) => {
const i = idx(e.target);
if (i > -1) {
e.target.setStyle(styles.basic);
results.splice(i, 1);
} else {
e.target.setStyle(styles.select);
results.push(e.target.feature.properties.Unit_Nm);
};
};
const pop = (e) => {
const txt = `
<div class="popup">
<h4> Public Land </h4>
<ul>
<li> <b>Name:</b> ${e.properties.Unit_Nm} </li>
<li> <b>Type:</b> ${e.properties.Own_Type} </li>
<li> <b>Agency:</b> ${e.properties.Own_Name} </li>
<li> <b>State:</b> ${e.properties.State_Nm} </li>
<li> <b>Size (acres):</b> ${e.properties.GIS_Acres} </li>
</ul>
</div>
`;
return txt;
};
const onEachFeature = (feature, layer) => {
layer.bindTooltip(pop(feature), {offset: L.point(30, 0)});
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: toggleFeature
});
}
let layer = L.geoJSON(fed_public_lands, {
style: styles.basic,
onEachFeature: onEachFeature
}).addTo(public_lands);
return layer;
}
mutable trigger = Mutable {}
trigger = Array(1) [0]
// Add public land name to results-container in side panel
displayResults = {
trigger;
let txt = "";
if (results.length > 0) { results.map(e => txt = txt + e + "<br>") };
d3.select(".results")
.selectAll("p")
.filter((d,i) => { return i == 0 })
.html(txt);
};
// Add sightings to eBird-container in side panel
displayeBird = {
// let d = updateLand[0];
let txt = "";
if (runAPI == null) {
txt = txt + "";
} else if (runAPI.length == 0) {
txt = txt + "No recent sightings <br> To update, click Run again";
} else if (runAPI.length > 0) {
let byName = d3.rollups(runAPI, v => d3.sum(v, d => d.howMany), d => d.comName);
let byName_v2 = byName.map(function(d) {
if (d[1] == 0) {
d[1] = 'undefined';
}
return(d)});
byName_v2.map(e => txt = txt + e[0] + ": " + e[1] + "<br>");
// runAPI.map(e => txt = txt + e.comName + ": " + e.howMany + "<br>");
// const names = [...new Set(runAPI.map(e => (e.locName)))];
// console.log(names);
};
// console.log(txt);
d3.select(".eBird")
.selectAll("p")
.filter((d,i) => { return i == 0 })
.html(txt);
};
geojson.eachLayer(e => {
let p = e.feature.properties;
let d = updateLand[0];
let b = updateLand[1];
let i = results.indexOf(p.Unit_Nm);
if (p.Unit_Nm == b) {
if (d == "add") {
if (i < 0) {
// e.setStyle(styles.select);
if (b == "Chesapeake and Ohio Canal National Historical Park") {
e.setStyle(styles.select_co);
} else {
e.setStyle(styles.select);
};
results.push(p.Unit_Nm);
mutable trigger += 1;
public_lands.fitBounds(e.getBounds());
};
} else {
if (i > -1) {
e.setStyle(styles.basic);
results.splice(i, 1);
mutable trigger -= 1;
};
};
} else { };
});
The boundaries for public lands are approximate and were obtained from the PAD-US database or elsewhere. Public land, here, refers to land that is accessible to the public and not necessarily public property.