Skip to content

Commit

Permalink
Timeline slider + more code cleanup
Browse files Browse the repository at this point in the history
- Added timeline slider
- Added more test markers
- Heavy code cleanup/organization
  • Loading branch information
mgkollander committed Mar 31, 2024
1 parent bfba420 commit d44a870
Show file tree
Hide file tree
Showing 9 changed files with 444 additions and 103 deletions.
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cSpell.words": [
"despawn"
]
}
Binary file added data/icons/pin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added favicon.ico
Binary file not shown.
10 changes: 10 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
<title>Alaska History Webmap</title>

<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.1/nouislider.min.css" integrity="sha512-qveKnGrvOChbSzAdtSs8p69eoLegyh+1hwOMbmpCViIwj7rn4oJjdmMvWOuyQlTOZgTlZA0N2PXA7iA8/2TUYA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.Default.css">
<link rel="stylesheet" href="styles.css">

<link rel="icon" type="image/x-icon" href="favicon.ico">

<!-- Prevent caching (to see GitHub Pages updates, might remove later) -->
<meta http-equiv='cache-control' content='no-cache'>
Expand All @@ -15,8 +20,13 @@
</head>
<body>
<div id="map"></div>

<div class="slider-styled" id="slider"></div>

<script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.1/nouislider.min.js" integrity="sha512-UOJe4paV6hYWBnS0c9GnIRH8PLm2nFK22uhfAvsTIqd3uwnWsVri1OPn5fJYdLtGY3wB11LGHJ4yPU1WFJeBYQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://unpkg.com/leaflet.markercluster/dist/leaflet.markercluster.js"></script>
<script src="mergeTooltips.js"></script>
<script src="map.js"></script>
</body>
</html>
213 changes: 148 additions & 65 deletions map.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,156 @@
// Initialize the map
function initializeMap() {
var map = L.map('map', {
maxBounds: [[46.56, -189.14], // SW corner
[73.15, -123.93]], // NE corner
maxBoundsViscosity: 1.0,
minZoom: calculateMinZoom()
}).setView([64.793, -153.040], calculateMinZoom());

// OSM layer
L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
maxZoom: 8,
attribution: '&copy; <a href="https://www.opentopomap.org/">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/" target="_blank">CC-BY-SA</a>) | Add names + data source'
}).addTo(map);

// Listen for screen resize event
window.addEventListener('resize', function(event) {
map.setMinZoom(calculateMinZoom());
});

return map;
}

// Calculate minZoom based on screen width
// Function to calculate minimum zoom based on screen width
function calculateMinZoom() {
var width = document.documentElement.clientWidth;
return width < 768 ? 4 : 5;
return document.documentElement.clientWidth < 768 ? 4 : 5;
}

// Load GeoJSON data and create markers
function loadMarkers(map) {
fetch('markerdata.geojson')
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch marker data');
// Function to filter markers based on current slider years
function filterMarkers() {
geojsonLayer.eachLayer(layer => {
const { startDate, endDate } = layer.feature.properties;
const inRange = startDate <= currentEndYear && endDate >= currentStartYear;
const fading = fadingMarkers.has(layer);
const icon = layer._icon;

if (inRange) {
if (fading) {
fadingMarkers.delete(layer);
if (icon) icon.classList.remove('fade-out');
}
if (icon && !icon.classList.contains('fade-in')) {
icon.classList.add('fade-in');
setTimeout(() => icon.classList.add('show'), 10);
}
return response.json();
})
.then(data => {
L.geoJSON(data, {
pointToLayer: function (feature, latlng) {
var marker = L.marker(latlng);
var popupContent = `
<div>
<h3>${feature.properties.description}</h3>
<p>${feature.properties.startDate} - ${feature.properties.endDate}</p>
</div>`;

var maxWidth = feature.properties.hasImage ? "auto" : feature.properties.maxWidth || 200;
if (feature.properties.hasImage) {
popupContent += `<img src="${feature.properties.imageUrl}" alt="Marker Image" style="max-width: 200px;">`;
}

marker.bindPopup(popupContent, { maxWidth: maxWidth });

// Add click event listener to marker, pan to on click
marker.on('click', function() {
map.panTo(marker.getLatLng());
});

return marker;
map.addLayer(layer);
} else {
if (!fading) {
fadingMarkers.add(layer);
if (icon) {
icon.classList.add('fade-out');
setTimeout(() => {
if (fadingMarkers.has(layer)) {
map.removeLayer(layer);
fadingMarkers.delete(layer);
}
}, 500);
}
}).addTo(map);
})
.catch(error => {
console.error('Error loading marker data:', error);
});
}
if (icon && icon.classList.contains('fade-in')) {
icon.classList.remove('fade-in', 'show');
}
}
});
}

// Function to initialize the slider
function initializeSlider() {
const slider = document.getElementById('slider');

// Destroy existing slider instance
if (slider.noUiSlider) slider.noUiSlider.destroy();

noUiSlider.create(slider, {
start: [currentStartYear, currentEndYear],
connect: true,
tooltips: [true, true],
format: { to: function (value) { return Math.round(value); },
from: function (value) { return value; }
},
range: { 'min': 1750, 'max': 2024 }
});

mergeTooltips(slider, Math.floor(9438/window.innerWidth), ' - ');

// Listen for slider changes
slider.noUiSlider.on('update', function(values) {
currentStartYear = parseInt(values[0]);
currentEndYear = parseInt(values[1]);
filterMarkers();
});
}

document.addEventListener('DOMContentLoaded', function() {
var map = initializeMap();
loadMarkers(map);
// Map initialization
const map = L.map('map', {
maxBounds: [[46.56, -189.14],[73.15, -123.93]],
maxBoundsViscosity: 0.5,
minZoom: calculateMinZoom(),
maxZoom: 8
}).setView([64.793, -153.040], calculateMinZoom());

// Add tile layer to map
L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.opentopomap.org/">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/" target="_blank">CC-BY-SA</a>)'
}).addTo(map);

// Set slider handle start and end years
let [currentStartYear, currentEndYear] = [1750, 2024];

// Set to keep track of markers currently fading out
let fadingMarkers = new Set();

// Load GeoJSON data
let geojsonLayer = L.geoJSON(null, {
onEachFeature: function(feature, layer) {
const properties = feature.properties;
const title = properties.startDate + " - " + properties.endDate;

let popupContent = "<h3>" + title + "</h3>";
popupContent += properties.description;
var maxWidth = feature.properties.hasImage ? "auto" : feature.properties.maxWidth || 200;
if (properties.hasImage) {
popupContent += "<img src='" + properties.imageUrl + "' alt='Marker Image' style='max-width: 200px;'>";
}

// Bind popup to marker layer
layer.bindPopup(popupContent, { maxWidth : maxWidth });

// Create a custom icon for the marker with no shadow
var customIcon = L.icon({
iconUrl: 'data/icons/pin.png', // URL to the marker icon image
iconSize: [30, 30], // Size of the icon image
iconAnchor: [10, 30], // Anchor point of the icon image
popupAnchor: [6, -30], // Popup anchor relative to the icon
shadowUrl: '', // No shadow URL
shadowSize: [0, 0], // No shadow size
shadowAnchor: [0, 0] // No shadow anchor
});

// Set the custom icon for the marker
layer.setIcon(customIcon);
}
}).addTo(map);

// Fetch GeoJSON data and add it to the map
fetch('markerdata.geojson')
.then(response => response.json())
.then(data => {
geojsonLayer.addData(data);
filterMarkers(); // Filter markers initially
});

// Initialize slider
initializeSlider();

// Listen for window resize event : reinitialize slider and calculate minZoom
window.addEventListener('resize', function() {
map.setMinZoom(calculateMinZoom());
initializeSlider();
});

/*
TO DO:
- Markers with uncertain location load a geographical location blob/circle
- Markers cluster when close together (check case of clustered markers changing number when moving range)
- Marker popup should have optional image, bold date, description under
- Marker popup should have two tabs, one for source or more info?
- About button top right, question mark icon that opens a new window (story board?) you can scroll through and exit out of
- GitHub link
- License
- Attribution
- Favicon
- Marker image
- noUISlider
- noUiSlider merge function
- General info + authors + data source
- Custom domain
- GitHub page organization
*/
Loading

0 comments on commit d44a870

Please sign in to comment.