Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge testing into main #1

Merged
merged 10 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .gitignore
Binary file not shown.
5 changes: 0 additions & 5 deletions .vscode/settings.json

This file was deleted.

Binary file added data/icons/cluster.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 modified 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
14 changes: 7 additions & 7 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>Alaska History Webmap</title>

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css" integrity="sha512-h9FcoyWjHcOcmEVkxOfTLnmZFWIH0iZhZT1H2TbOq55xssQGEJHEaIm+PgoUaZbRvQTNTluNOEfb1ZRy6D3BOw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prunecluster/2.1.0/LeafletStyleSheet.min.css" integrity="sha512-ZTsIKR9XmEgyAXmXVRpE+wvv2+72Kd0tBSieJGbe1oOydyEmRhzTWEIfpLAlC8IYxNhGq2eALt+G/kRgpqlzyA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<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="styles.css">
<link rel="icon" type="image/x-icon" href="favicon.ico">

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

<!-- Prevent caching (to see GitHub Pages updates, might remove later) -->
<meta http-equiv='cache-control' content='no-cache'>
Expand All @@ -19,10 +20,9 @@
<body>
<div id="map"></div>
<div class="slider-styled" id="slider"></div>

<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js" integrity="sha512-puJW3E/qXDqYp9IfhAI54BJEaWIfloJ7JWs7OeD5i6ruC9JZL1gERT1wjtwXFlh7CjE7ZJ+/vcRZRkIYIb6p4g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prunecluster/2.1.0/PruneCluster.min.js" integrity="sha512-TIhw+s9KAwyAGM7n2qG2hM+lvQxja1Hieb3nS3F2y9AFEDImo6SNXoooqmajF/D5lMfriBIasQ6N+pizlF0wTA==" crossorigin="anonymous" referrerpolicy="no-referrer"></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="mergeTooltips.js"></script>
<script src="map.js"></script>
</body>
Expand Down
257 changes: 146 additions & 111 deletions map.js
Original file line number Diff line number Diff line change
@@ -1,135 +1,170 @@
// Function to calculate minimum zoom based on screen width
function calculateMinZoom() {
return document.documentElement.clientWidth < 768 ? 4 : 5;
}
// Function to calculate minimum zoom based on current screen width
const calculateMinZoom = () => document.documentElement.clientWidth < 768 ? 4 : 5;

// 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);
}
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);
}
}
if (icon && icon.classList.contains('fade-in')) {
icon.classList.remove('fade-in', 'show');
}
// Initialize map
const map = L.map('map', {
maxBounds:[[46.56, -189.14],[73.15, -123.93]],
maxBoundsViscosity: 0.5,
minZoom: calculateMinZoom(),
maxZoom: 8,
attributionControl: false // Will attribute on "About" page
}).setView([64.793, -153.040], calculateMinZoom());

// Add tile layer
L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png').addTo(map);

// Initialize PruneCluster
const leafletView = new PruneClusterForLeaflet();
leafletView.Cluster.Size = 150;

// Customize marker behavior
leafletView.PrepareLeafletMarker = function(leafletMarker, data) {
let popupContent = "<b>" + data.properties.startDate + ' - ' + data.properties.endDate + "</b><br>" + data.properties.description;
const customIcon = L.icon({
iconUrl: 'data/icons/pin.png',
iconSize: [40, 40],
iconAnchor: [3, 30],
popupAnchor: [17, -28]
});
// Set custom icon and popups
leafletMarker.setIcon(customIcon);
leafletMarker.bindPopup(popupContent);
};

// Set custom cluster icon
leafletView.BuildLeafletClusterIcon = function(cluster) {
let population = cluster.population;
let icon = L.divIcon({
html: '<div class="cluster-icon">' + population + '</div>',
className: 'custom-cluster-icon',
iconSize: [40, 40],
iconAnchor: [20, 40]
});
return icon;
};

// Create layer group
let pruneClusterLayer = L.layerGroup();

// Array to store marker data
let markerData = [];
let markersOnMapFeatures = [];
let markersOnMapMarkers = [];

// Fetch GeoJSON and store marker data
const fetchMarkerData = async () => {
const response = await fetch('markerData.geojson');
const data = await response.json();
return data.features;
};

// Create a new marker
const createMarker = (feature) => {
const marker = new PruneCluster.Marker(feature.geometry.coordinates[1], feature.geometry.coordinates[0]);
marker.data.properties = feature.properties;
leafletView.RegisterMarker(marker);
return marker;
};

// Add a marker to the map
const addMarkerToMap = (feature, marker) => {
markersOnMapFeatures.push(feature);
markersOnMapMarkers.push(marker);
};

// Remove a marker from the map
const removeMarkerFromMap = (feature, marker) => {
markersOnMapFeatures = markersOnMapFeatures.filter(item => item !== feature);
markersOnMapMarkers = markersOnMapMarkers.filter(item => item !== marker);
leafletView.RemoveMarkers([marker]);
};

// Add markers in initial slider range
const addInitialMarkers = (markerData) => {
markerData.forEach(feature => {
if (feature.properties.startDate <= 1800 && feature.properties.endDate >= 1750) {
const marker = createMarker(feature);
addMarkerToMap(feature, marker);
}
});
}
};

// Function to initialize the slider
fetchMarkerData().then(data => {
// Store in array
markerData = data;

addInitialMarkers(markerData);

// Add layer group to map
map.addLayer(leafletView);
leafletView.ProcessView();
});

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

// Destroy existing slider instance
if (slider.noUiSlider) slider.noUiSlider.destroy();
// If slider already initialized, destroy it
if (slider.noUiSlider) {
slider.noUiSlider.destroy();
}

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

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

// Listen for slider changes
slider.noUiSlider.on('update', function(values) {
currentStartYear = parseInt(values[0]);
currentEndYear = parseInt(values[1]);
filterMarkers();
// Set title attribute for each slider handle
let sliderHandles = document.querySelectorAll('.noUi-handle-lower, .noUi-handle-upper');
sliderHandles.forEach((handle, index) => {
let handleName = index == 0 ? 'Lower' : 'Upper';
handle.setAttribute('title', `${handleName} slider control for adjusting markers on map`);
});
}

// Map initialization
const map = L.map('map', {
maxBounds: [[46.56, -189.14],[73.15, -123.93]],
maxBoundsViscosity: 0.5,
minZoom: calculateMinZoom(),
maxZoom: 8,
attributionControl: false
}).setView([64.793, -153.040], calculateMinZoom());

// Add tile layer to map
L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png').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();
// Merge slider tooltips when overlapping
mergeTooltips(slider, Math.floor(8600 / window.innerWidth), ' - ');

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

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;'>";
// Initialize slider
const slider = initializeSlider();

// Function to filter markers by slider range
const filterMarkersByRange = (sliderStartYear, sliderEndYear) => {
markerData.forEach(feature => {
// If in range
if (feature.properties.startDate <= sliderEndYear && feature.properties.endDate >= sliderStartYear) {
// If not already on map, add to map
if (!markersOnMapFeatures.includes(feature)) {
const marker = createMarker(feature);
addMarkerToMap(feature, marker);
}
} else {
// Else if not in range and on map, remove from map
if (markersOnMapFeatures.includes(feature)) {
const marker = markersOnMapMarkers.find(marker => marker.data.properties === feature.properties);
removeMarkerFromMap(feature, marker);
}
}
});
leafletView.ProcessView();
};

// 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: [64, 64], // Size of the icon image
iconAnchor: [10, 30], // Anchor point of the icon image
popupAnchor: [22, -28], // 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
});
// Add event listener for slider slide event
slider.noUiSlider.on('slide', function(values) {
// Get current slider values
let sliderStartYear = parseInt(values[0]);
let sliderEndYear = parseInt(values[1]);

// Initialize slider
initializeSlider();
filterMarkersByRange(sliderStartYear, sliderEndYear);
});

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