// TODO split into smaller cells
map = {
// create map object
let map = L.map(viewof container);
map.on('load', function(event){
// get the width of the screen after the resize event
var width = document.documentElement.clientWidth;
// tablets are between 768 and 922 pixels wide
// phones are less than 768 pixels wide
if (width < 768) {
map.setZoom(4);
}
});
map.setView([62.945279601222396, -155.5946697727831], 5);
// https://github.com/kbvernon/hndsr-watersheds/blob/main/observable-map.qmd#L122
// add basemap
window.___MAP = map;
map.whenReady(()=> {
//console.log('whenReady fired');
//fetch('whenreadyfired.com')
//show_sidebar();
});
// Tell leaflet that we closed/opened sidebar which caused map width to change
addEventListener('sidebar-closed', ()=>{
console.log('map recv sidebar close');
previouslyClickedLayer.setStyle(myStyle);
previouslyClickedLayer = null;
map.invalidateSize();
});
addEventListener('sidebar-opened', ()=>{
console.log('map recv sidebar open');
map.invalidateSize();
});
const osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>'});
// osm.addTo(map);
const esri_topo = L.tileLayer(
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
{attribution: 'Tiles © Esri — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'});
// esri_topo.addTo(map);
// https://geoportal.alaska.gov/portal/home/item.html?id=d462231cc1454e1abb2dccd9a709a476
const ak_2020_imagery = L.tileLayer(
'https://geoportal.alaska.gov/arcgis/rest/services/ahri_2020_rgb_cache/MapServer/tile/{z}/{y}/{x}?blankTile=false', {
//layers: '0',
//format: 'image/png',
attribution: 'Dynamic Mosaic © 2020 Maxar Technologies Inc., Alaska Geospatial Office, USGS'});
ak_2020_imagery.addTo(map);
const aedg_communities = null;
L.control.layers(
{
"OpenStreetMap": osm,
"ESRI Topographic": esri_topo,
"Alaska High Resolution Imagery (50cm)": ak_2020_imagery
},
null,
{position: 'topleft'}
).addTo(map);
L.control.scale({imperial: true, metric: true}).addTo(map);
//let communityMarkers = new L.FeatureGroup();
let communities = [];
// https://github.com/HandsOnDataViz/leaflet-map-csv/blob/main/index.html
fetch('https://raw.githubusercontent.com/acep-aedg/aedg-data-pond/refs/heads/main/data/public/public_communities/public_communities.csv')
.then((response) => response.text())
.then((csvString) => {
let data = Papa.parse(csvString, {header: true, dynamicTyping: true, skipEmptyLines: true}).data;
for (let i in data) {
let row = data[i];
/*let marker = L.circleMarker([row.latitude, row.longitude], {
radius: 4,
opacity: 1,
color: '#000',
stroke: false,
fillOpacity: 0.75
}).bindTooltip(row.name);*/
communities.push({name: row.name, lat: row.latitude, lon: row.longitude, shown: false});
//communityMarkers.addLayer(marker);
//marker.addTo(map);
}
})
// https://leafletjs.com/examples/geojson/
// Add this last?
var myStyle = {
"color": "#ff932e",
//"weight": 5,
//"opacity": 0.65
};
var highlight = {
"color": "#FF0000"
};
let previouslyClickedLayer = null;
L.geoJSON(service_areas, {
style: myStyle,
onEachFeature: function (feature, layer) {
layer.on('click', function (e) {
console.log('Feature clicked:');
console.log(feature);
if (previouslyClickedLayer && previouslyClickedLayer !== layer) {
previouslyClickedLayer.setStyle(myStyle);
}
layer.setStyle(highlight);
previouslyClickedLayer = layer;
map.flyToBounds(layer.getBounds(), {duration: 0.4});
// map.setView(layer.getBounds().getCenter());
mutable name = layer.feature.properties.certificate_name;
mutable cpcn_url = layer.feature.properties.certificate_url;
mutable granted_year = layer.feature.properties.certificate_granted_year;
mutable should_show_welcome_message = false;
show_sidebar();
// TODO: show list of communities/places within service area (served by clicked utility). Could check which pins are in bounds, but better approach will be to use AEDG data. In sidebar, city names should be hyperlinked, when you click the name it will zoom the map to that city (useful for utilities that service many cities).
// Also, button to return to starting view of entire state
});
}
}).addTo(map);
let currentMarkers = [];
// Instead of hiding all marker layers based on zoom level as shown in the link, which has poor performance on my phone, selectively show markers based on position in map bounds
// https://gis.stackexchange.com/questions/258515/show-hide-markers-depending-on-zoom-level
function renderMarkers() {
if (map.getZoom() >= 7) { // maybe 8 if desktop and 7 if mobile/small screen
// Adapted from https://medium.com/@silvajohnny777/optimizing-leaflet-performance-with-a-large-number-of-markers-0dea18c2ec99
// Get the current map bounds
const bounds = map.getBounds();
// Temp comment this out, I feel like it's probably OK for the user to scroll around and keep the communities scrolled out of view on the map
// Remove old markers
// currentMarkers.forEach((marker) => map.removeLayer(marker));
// currentMarkers.length = 0;
// Render only markers that are within the map bounds
communities.filter((x)=>!x.shown).forEach((community) => {
const position = [community.lat, community.lon];
// Check if the community is within the current map bounds
if (bounds.contains(position)) {
let marker = L.marker(position, {
opacity: 1,
color: 'red'
}).bindTooltip(community.name);
// console.log("Added marker for " + community.name);;
marker.addTo(map);
currentMarkers.push(marker);
community.shown = true;
}
});
} else {
// Clear existing markers if zoomed out
currentMarkers.forEach((marker) => map.removeLayer(marker));
currentMarkers.length = 0;
communities.forEach((community) => community.shown = false);
}
};
map.on('moveend', renderMarkers);
map.on('zoomend', renderMarkers);
// TODO: add legend https://stackoverflow.com/questions/59453642/how-to-add-legend-in-leaflet-map
}