From d44a87029663693fc946a41238f8d46523ce13de Mon Sep 17 00:00:00 2001 From: Mary Kollander <124931791+mgkollander@users.noreply.github.com> Date: Sat, 30 Mar 2024 22:11:37 -0800 Subject: [PATCH] Timeline slider + more code cleanup - Added timeline slider - Added more test markers - Heavy code cleanup/organization --- .vscode/settings.json | 5 + data/icons/pin.png | Bin 0 -> 6099 bytes {images => data/images}/cat.JPG | Bin favicon.ico | Bin 0 -> 4286 bytes index.html | 10 ++ map.js | 213 ++++++++++++++++++++++---------- markerdata.geojson | 175 ++++++++++++++++++++------ mergeTooltips.js | 81 ++++++++++++ styles.css | 63 +++++++++- 9 files changed, 444 insertions(+), 103 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 data/icons/pin.png rename {images => data/images}/cat.JPG (100%) create mode 100644 favicon.ico create mode 100644 mergeTooltips.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d546ef0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "despawn" + ] +} \ No newline at end of file diff --git a/data/icons/pin.png b/data/icons/pin.png new file mode 100644 index 0000000000000000000000000000000000000000..d0c027deb90f4eec6574d6ca6b41a9140c91bd67 GIT binary patch literal 6099 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 literal 0 HcmV?d00001 diff --git a/images/cat.JPG b/data/images/cat.JPG similarity index 100% rename from images/cat.JPG rename to data/images/cat.JPG diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4c0835dd27f3685b2774e627287eed47fe32b495 GIT binary patch literal 4286 zcmeHJO=uHA6dwPmD6}>^le9%`MZt<56od*27DW(IPZo*?t=Y+1kqVxA@&`Rg1@+*? zL#YZLq!;m`BBrxTrF!T=1Q7+L5)eVL5kXrkj^EpKAa1wW-5dn52`{tz-uK@3=Iwj4 zVQe10EiH_G+3jVFr5R(b5U5x?g!KLgHfw1~N{YGP|Gfgrw%T1sbhvhU*Qmo51?w?l z*QW9^v)<^3o%)sLx|&rTZeFZ%qTiF^xRR;m`D{b8>+ln<6oYiHWO|F6;{!^H9=gtV zH?1w_f1I_NhMe?H;Cm@n(i{-?fSVHNG9^-y7dICkiT?*%0MZZdZ=g3g2<^N6wSj$KG;#oNMI24R!cDox^w>AHobD ztE&g+E8kD@PeV?oEv~P@QJ>L_pawVnJ;-!7**wW@seFd=B)kxO!+?I z5-=~|dy@Lu#EiR^WO z?b_Bp#5ZR}sqR$!gc24%u8z*cq*f55^3;M}PIX zHs2Fw8?wb#zlRgn-Q*D#vsxW$an;0)yC&trd4DSJh}B_!RHL}O1{W4*HXZ7G5qrF^ z&#?Ih1Kk4?tD4osiL*Yk)0rR+`P?bvrFZt-virz+48}`(PiLn;hf-XA+LNjcKMQ9+ z7n1T-!s9*GpJeZZtrRbbjd}v_u(t{M3rRRCVd*~vYX2>gU-FO27DwnEOz+}KF{`op z|5qh{HX{!yUZPKW)zM5#HF1;p(D?xVE0~4XcqR`aoqvNjNse>af1?6A@TqIlMYd5F ziw$~AFVvnskMFRq(dSql4wLQrEYp*W=?Ta$$N=OzM2|836@r1+_;ap-^>y0CN>Z%R KdZIbNruA?AIv9Ka literal 0 HcmV?d00001 diff --git a/index.html b/index.html index 5ee41c1..f04af8d 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,12 @@ Alaska History Webmap + + + + + @@ -15,8 +20,13 @@
+ +
+ + + diff --git a/map.js b/map.js index 35d00a4..50815bc 100644 --- a/map.js +++ b/map.js @@ -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: '© OpenTopoMap (CC-BY-SA) | 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 = ` -
-

${feature.properties.description}

-

${feature.properties.startDate} - ${feature.properties.endDate}

-
`; - - var maxWidth = feature.properties.hasImage ? "auto" : feature.properties.maxWidth || 200; - if (feature.properties.hasImage) { - popupContent += `Marker Image`; - } - - 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: '© OpenTopoMap (CC-BY-SA)' +}).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 = "

" + title + "

"; + popupContent += properties.description; + var maxWidth = feature.properties.hasImage ? "auto" : feature.properties.maxWidth || 200; + if (properties.hasImage) { + popupContent += "Marker Image"; + } + + // 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 +*/ diff --git a/markerdata.geojson b/markerdata.geojson index cb938e2..0bbdc02 100644 --- a/markerdata.geojson +++ b/markerdata.geojson @@ -1,59 +1,162 @@ { - "type": "FeatureCollection", - "features": [ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "description": "My cat Muffin", + "startDate": 2011, + "endDate": 2024, + "hasImage": true, + "imageUrl": "data/images/cat.JPG", + "hasSetLocation": true + }, + "geometry": { + "type": "Point", + "coordinates": [-149.6812, 61.1107] + } + }, + { + "type": "Feature", + "properties": { + "description": "Test marker 1", + "startDate": 1750, + "endDate": 1780, + "hasImage": false, + "hasSetLocation": true + }, + "geometry": { + "type": "Point", + "coordinates": [-159.040, 67.793] + } + }, + { + "type": "Feature", + "properties": { + "description": "Test marker 2", + "startDate": 1800, + "endDate": 1810, + "hasImage": false, + "hasSetLocation": false + }, + "geometry": { + "type": "Point", + "coordinates": [-154.040, 62.793] + } + }, + { + "type": "Feature", + "properties": { + "description": "Default", + "startDate": 2023, + "endDate": 2024, + "hasImage": false, + "hasSetLocation": false + }, + "geometry": { + "type": "Point", + "coordinates": [-148.7349, 63.9646] + } + }, + { + "type": "Feature", + "properties": { + "description": "Test marker 3", + "startDate": 1830, + "endDate": 1840, + "hasImage": true, + "imageUrl": "data/images/cat.JPG", + "hasSetLocation": true + }, + "geometry": { + "type": "Point", + "coordinates": [-149.00, 64.00] + } + }, + { + "type": "Feature", + "properties": { + "description": "Test marker 4", + "startDate": 1860, + "endDate": 1870, + "hasImage": false, + "hasSetLocation": true + }, + "geometry": { + "type": "Point", + "coordinates": [-150.00, 65.00] + } + }, + { + "type": "Feature", + "properties": { + "description": "Test marker 5", + "startDate": 1890, + "endDate": 1900, + "hasImage": false, + "hasSetLocation": false + }, + "geometry": { + "type": "Point", + "coordinates": [-151.00, 66.00] + } + }, { "type": "Feature", "properties": { - "description": "My cat Muffin", - "startDate": 2011, - "endDate": "Present", - "hasImage": true, - "imageUrl": "images/cat.JPG" + "description": "My cat Muffin", + "startDate": 2009, + "endDate": 2023, + "hasImage": true, + "imageUrl": "data/images/cat.JPG", + "hasSetLocation": true }, "geometry": { - "type": "Point", - "coordinates": [-149.6812, 61.1107] + "type": "Point", + "coordinates": [-146.6812, 61.1107] } - }, - { + }, + { "type": "Feature", "properties": { - "description": "Test marker 1", - "startDate": 1750, - "endDate": 1780, - "hasImage": false + "description": "Test marker 1", + "startDate": 1750, + "endDate": 1780, + "hasImage": false, + "hasSetLocation": true }, "geometry": { - "type": "Point", - "coordinates": [-159.040, 67.793] + "type": "Point", + "coordinates": [-159.040, 67.793] } - }, - { + }, + { "type": "Feature", "properties": { - "description": "Test marker 2", - "startDate": 1800, - "endDate": 1810, - "hasImage": false + "description": "Test marker 2", + "startDate": 1800, + "endDate": 1810, + "hasImage": false, + "hasSetLocation": false }, "geometry": { - "type": "Point", - "coordinates": [-154.040, 62.793] + "type": "Point", + "coordinates": [-154.040, 62.793] } - }, - { + }, + { "type": "Feature", "properties": { - "description": "Default", - "startDate": 2024, - "endDate": 2024, - "hasImage": false + "description": "Default", + "startDate": 2023, + "endDate": 2024, + "hasImage": false, + "hasSetLocation": false }, "geometry": { - "type": "Point", - "coordinates": [-148.7349, 63.9646] + "type": "Point", + "coordinates": [-148.7349, 63.9646] } - } - ] + } + ] } - diff --git a/mergeTooltips.js b/mergeTooltips.js new file mode 100644 index 0000000..e8d2d2d --- /dev/null +++ b/mergeTooltips.js @@ -0,0 +1,81 @@ +/** + * From https://refreshless.com/nouislider/examples/ + * + * @param slider HtmlElement with an initialized slider + * @param threshold Minimum proximity (in percentages) to merge tooltips + * @param separator String joining tooltips + */ +function mergeTooltips(slider, threshold, separator) { + + var textIsRtl = getComputedStyle(slider).direction === 'rtl'; + var isRtl = slider.noUiSlider.options.direction === 'rtl'; + var isVertical = slider.noUiSlider.options.orientation === 'vertical'; + var tooltips = slider.noUiSlider.getTooltips(); + var origins = slider.noUiSlider.getOrigins(); + + // Move tooltips into the origin element. The default stylesheet handles this. + tooltips.forEach(function (tooltip, index) { + if (tooltip) { + origins[index].appendChild(tooltip); + } + }); + + slider.noUiSlider.on('update', function (values, handle, unencoded, tap, positions) { + + var pools = [[]]; + var poolPositions = [[]]; + var poolValues = [[]]; + var atPool = 0; + + // Assign the first tooltip to the first pool, if the tooltip is configured + if (tooltips[0]) { + pools[0][0] = 0; + poolPositions[0][0] = positions[0]; + poolValues[0][0] = values[0]; + } + + for (var i = 1; i < positions.length; i++) { + if (!tooltips[i] || (positions[i] - positions[i - 1]) > threshold) { + atPool++; + pools[atPool] = []; + poolValues[atPool] = []; + poolPositions[atPool] = []; + } + + if (tooltips[i]) { + pools[atPool].push(i); + poolValues[atPool].push(values[i]); + poolPositions[atPool].push(positions[i]); + } + } + + pools.forEach(function (pool, poolIndex) { + var handlesInPool = pool.length; + + for (var j = 0; j < handlesInPool; j++) { + var handleNumber = pool[j]; + + if (j === handlesInPool - 1) { + var offset = 0; + + poolPositions[poolIndex].forEach(function (value) { + offset += 1000 - value; + }); + + var direction = isVertical ? 'bottom' : 'right'; + var last = isRtl ? 0 : handlesInPool - 1; + var lastOffset = 1000 - poolPositions[poolIndex][last]; + offset = (textIsRtl && !isVertical ? 100 : 0) + (offset / handlesInPool) - lastOffset; + + // Center this tooltip over the affected handles + tooltips[handleNumber].innerHTML = poolValues[poolIndex].join(separator); + tooltips[handleNumber].style.display = 'block'; + tooltips[handleNumber].style[direction] = offset + '%'; + } else { + // Hide this tooltip + tooltips[handleNumber].style.display = 'none'; + } + } + }); + }); +} \ No newline at end of file diff --git a/styles.css b/styles.css index 2583c71..d5d3519 100644 --- a/styles.css +++ b/styles.css @@ -1,8 +1,67 @@ -body { +*, :after, :before { + box-sizing: border-box; padding: 0; margin: 0; } -html, body, #map { + +html, body { height: 100%; + width: 100%; +} + +body { + position: relative; + min-height: 100%; + margin: 0; + padding: 0; + background-color: #f1f1f1; +} + +#map { + position: relative; width: 100vw; + height: 100vh; + overflow: hidden; +} + +#slider { + position: absolute; + bottom: 40px; + left: 50px; + z-index: 1000; + width: 50%; + height: 10px; +} + +.fade-out, +.fade-in { + opacity: 0; + transition: opacity 0.2s ease; +} + +.fade-in.show { + opacity: 1; +} + +.slider-styled .noUi-handle { + box-shadow: none; + border-color: #70707060; + border-radius: 5px; + height: 18px; + width: 18px; + top: -5px; + right: -9px; +} + +.slider-styled .noUi-handle::before, +.slider-styled .noUi-handle::after { + display: none; +} + +#slider .noUi-connect { + background: #a3000098; +} + +.noUi-tooltip { + font-family: "Roboto", sans-serif; }