2025-03-06 00:58:29 -05:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
2025-04-04 00:13:16 -04:00
< title > Gas Prices< / title >
< meta name = "description" content = "Track the gas prices of Sam's Club and Costco locations across the United States" >
2025-03-26 23:04:03 -04:00
< meta name = "viewport" content = "width=device-width,initial-scale=1" >
2025-04-07 02:33:50 -04:00
< link rel = "icon" href = "gas.svg" type = "image/svg+xml" >
2025-04-10 00:26:15 -04:00
< script src = "https://static.stevenalexander.org/leaflet/leaflet.js" integrity = "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin = "anonymous" > < / script >
< link rel = "stylesheet" href = "https://static.stevenalexander.org/leaflet/leaflet.css" integrity = "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin = "anonymous" >
2025-03-26 23:04:03 -04:00
< base target = "_blank" >
< style > h t m l , b o d y { h e i g h t : 1 0 0 % ; m a r g i n : 0 ; } # m a p { h e i g h t : 1 0 0 v h ; }
.leaflet-container a{
color:black;
text-decoration:none;
2025-03-06 00:58:29 -05:00
}
2025-04-20 11:34:54 -04:00
.samsclub,.costco{
2025-03-26 23:04:03 -04:00
display:flex;
border-radius:33%;
text-align:center;
align-items:center;
font-size:14px;
font-weight:bold;
2025-03-06 00:58:29 -05:00
}
2025-04-20 11:34:54 -04:00
.samsclub{background-color:rgba(0,103,160,0.5);}
.costco{background-color:rgba(227,42,54,0.5);}
2025-03-06 00:58:29 -05:00
< / style >
< / head >
< body >
2025-03-26 23:04:03 -04:00
< div id = "map" > < / div >
2025-03-06 00:58:29 -05:00
< script >
2025-04-20 11:34:54 -04:00
let map=L.map("map",{center:[38.243,-85.647],zoom:11,layers:[L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png")]});
let layerControl=L.control.layers(null,null,{collapsed:false}).addTo(map);
let chains={"Sam's Club":"https://www.samsclub.com/local/fuel-center/-/X","Costco":"https://www.costco.com/warehouse-locations-X.html#:~:text=Gas%20Station"};
for (const [chain,url] of Object.entries(chains)){
const chainId=chain.toLowerCase().replace(/[^a-z]/g,'');
chains[chain]=L.layerGroup().addTo(map);
layerControl.addOverlay(chains[chain],`${chain} loading...`)
fetch(chainId+".csv").then(response=>{
if(!response.ok){throw new Error(response.status)}
return response.text()
}).then(csv=>{
csv.split("\n").forEach(line=>{
2025-04-07 02:33:50 -04:00
let [u,p,lat,lng,id]=line.split(",");
2025-03-31 01:57:26 -04:00
[u,p]=[u,p].map(cents=>"$"+(cents/100).toFixed(2));
2025-04-20 11:34:54 -04:00
L.marker([lat,lng],{icon:L.divIcon({className:chainId,html:`< a href = "${url.replace(" X " , id ) } " > ${u}\n${p}< / a > `,iconSize:[44,44]})}).addTo(chains[chain])
})
layerControl.removeLayer(chains[chain]);
layerControl.addOverlay(chains[chain],`${chain} (${chains[chain].getLayers().length} locations)`)
}).catch(error=>{
console.error(error);
layerControl.removeLayer(chains[chain]);
layerControl.addOverlay(L.layerGroup(),`${chain} request was blocked :(`)
2025-03-06 00:58:29 -05:00
})
2025-04-20 11:34:54 -04:00
}
2025-04-20 11:36:19 -04:00
const lastPosition=localStorage.getItem("lastPosition");
if(lastPosition){
const pos=JSON.parse(lastPosition);
map.setView(pos.coords,pos.zoom);
}
map.on("moveend zoomend",function(){
const center=map.getCenter();
localStorage.setItem("lastPosition",JSON.stringify({coords:[center.lat,center.lng],zoom:map.getZoom()}));
});
const ResetViewControl=L.Control.extend({
onAdd:function(map){
const btn=L.DomUtil.create('button','leaflet-bar leaflet-control');
btn.innerHTML='⌂';
btn.title='Reset view';
btn.style.backgroundColor='white';
btn.style.font='22px bold';
btn.style.cursor='pointer';
L.DomEvent.on(btn,'click',function(e){
localStorage.removeItem("lastPosition");
location.reload();
});
return btn;
}
});
map.addControl(new ResetViewControl({position:'topleft'}));
2025-03-06 00:58:29 -05:00
< / script >
< / body >
< / html >