geo-morpher 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
createMapLibreCustomGlyphLayer,
|
|
6
6
|
parseCSV,
|
|
7
7
|
WGS84Projection,
|
|
8
|
-
} from "
|
|
9
|
-
import { flattenPositions } from "
|
|
8
|
+
} from "../../src/index.js";
|
|
9
|
+
import { flattenPositions } from "../../src/adapters/shared/geometry.js";
|
|
10
10
|
|
|
11
11
|
const metrics = [
|
|
12
12
|
{
|
|
@@ -90,12 +90,18 @@ const tooltipEl = document.getElementById("tooltip");
|
|
|
90
90
|
const statusEl = document.getElementById("status");
|
|
91
91
|
const slider = document.getElementById("morphFactor");
|
|
92
92
|
const factorValue = document.getElementById("factorValue");
|
|
93
|
+
const playButton = document.getElementById("play-button");
|
|
94
|
+
const playIcon = document.getElementById("play-icon");
|
|
95
|
+
const pauseIcon = document.getElementById("pause-icon");
|
|
93
96
|
const glyphLegendEl = document.getElementById("glyphLegend");
|
|
94
97
|
const regularToggle = document.getElementById("toggle-regular");
|
|
95
98
|
const interpolatedToggle = document.getElementById("toggle-interpolated");
|
|
96
99
|
const cartogramToggle = document.getElementById("toggle-cartogram");
|
|
97
100
|
const glyphToggle = document.getElementById("toggle-glyphs");
|
|
98
101
|
let hasBootstrapped = false;
|
|
102
|
+
let isPlaying = false;
|
|
103
|
+
let animationFrameId = null;
|
|
104
|
+
let animationDirection = 1; // 1 for forward, -1 for backward
|
|
99
105
|
|
|
100
106
|
// comparison mode state --------------------------------------------------
|
|
101
107
|
// when a province/glyph is clicked we compute an outline drawing function
|
|
@@ -374,7 +380,9 @@ async function bootstrap() {
|
|
|
374
380
|
hasBootstrapped = true;
|
|
375
381
|
|
|
376
382
|
try {
|
|
377
|
-
statusEl
|
|
383
|
+
if (statusEl) {
|
|
384
|
+
statusEl.textContent = "Loading data…";
|
|
385
|
+
}
|
|
378
386
|
|
|
379
387
|
const [regularGeoJSON, cartogramCSV, literacyCSV] = await Promise.all([
|
|
380
388
|
fetchJSON("indonesia/indonesia_provice_boundary.geojson"),
|
|
@@ -551,17 +559,19 @@ async function bootstrap() {
|
|
|
551
559
|
if (features.length > 0) {
|
|
552
560
|
const feature = features[0];
|
|
553
561
|
|
|
554
|
-
if (hoveredStateId !== null) {
|
|
562
|
+
if (hoveredStateId !== null && hoveredStateId !== undefined) {
|
|
555
563
|
map.setFeatureState(
|
|
556
564
|
{ source: morphControls.sourceIds.interpolated, id: hoveredStateId },
|
|
557
565
|
{ hover: false }
|
|
558
566
|
);
|
|
559
567
|
}
|
|
560
568
|
hoveredStateId = feature.id;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
569
|
+
if (hoveredStateId !== null && hoveredStateId !== undefined) {
|
|
570
|
+
map.setFeatureState(
|
|
571
|
+
{ source: morphControls.sourceIds.interpolated, id: hoveredStateId },
|
|
572
|
+
{ hover: true }
|
|
573
|
+
);
|
|
574
|
+
}
|
|
565
575
|
|
|
566
576
|
// Use featureId or properties.id to join with morpher data
|
|
567
577
|
const id = feature.properties.id || feature.properties.ID;
|
|
@@ -588,7 +598,7 @@ async function bootstrap() {
|
|
|
588
598
|
if (map?.getCanvas) {
|
|
589
599
|
map.getCanvas().style.cursor = "";
|
|
590
600
|
}
|
|
591
|
-
if (hoveredStateId !== null) {
|
|
601
|
+
if (hoveredStateId !== null && hoveredStateId !== undefined) {
|
|
592
602
|
map.setFeatureState(
|
|
593
603
|
{ source: morphControls.sourceIds.interpolated, id: hoveredStateId },
|
|
594
604
|
{ hover: false }
|
|
@@ -635,11 +645,11 @@ async function bootstrap() {
|
|
|
635
645
|
map.fitBounds(bounds, { padding: 48, linear: true, duration: 0 });
|
|
636
646
|
}
|
|
637
647
|
|
|
638
|
-
|
|
639
|
-
|
|
648
|
+
const updateUIForFactor = (value) => {
|
|
649
|
+
slider.value = value;
|
|
640
650
|
factorValue.textContent = value.toFixed(2);
|
|
641
651
|
morphControls.updateMorphFactor(value);
|
|
642
|
-
|
|
652
|
+
|
|
643
653
|
// Update OSM opacity
|
|
644
654
|
map.setPaintProperty("osm", "raster-opacity", 0.2 * (1 - value));
|
|
645
655
|
|
|
@@ -652,13 +662,70 @@ async function bootstrap() {
|
|
|
652
662
|
if (!glyphToggle || glyphToggle.checked) {
|
|
653
663
|
glyphControls.updateGlyphs({ morphFactor: value });
|
|
654
664
|
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
slider.addEventListener("input", (event) => {
|
|
668
|
+
const value = Number(event.target.value);
|
|
669
|
+
if (isPlaying) {
|
|
670
|
+
stopAnimation();
|
|
671
|
+
}
|
|
672
|
+
updateUIForFactor(value);
|
|
655
673
|
});
|
|
656
674
|
|
|
657
|
-
|
|
675
|
+
const animate = () => {
|
|
676
|
+
if (!isPlaying) return;
|
|
677
|
+
|
|
678
|
+
let currentValue = Number(slider.value);
|
|
679
|
+
const step = 0.005;
|
|
680
|
+
|
|
681
|
+
currentValue += step * animationDirection;
|
|
682
|
+
|
|
683
|
+
if (currentValue >= 1) {
|
|
684
|
+
currentValue = 1;
|
|
685
|
+
animationDirection = -1;
|
|
686
|
+
} else if (currentValue <= 0) {
|
|
687
|
+
currentValue = 0;
|
|
688
|
+
animationDirection = 1;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
updateUIForFactor(currentValue);
|
|
692
|
+
animationFrameId = requestAnimationFrame(animate);
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
const startAnimation = () => {
|
|
696
|
+
isPlaying = true;
|
|
697
|
+
playIcon.style.display = "none";
|
|
698
|
+
pauseIcon.style.display = "block";
|
|
699
|
+
animate();
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
const stopAnimation = () => {
|
|
703
|
+
isPlaying = false;
|
|
704
|
+
playIcon.style.display = "block";
|
|
705
|
+
pauseIcon.style.display = "none";
|
|
706
|
+
if (animationFrameId) {
|
|
707
|
+
cancelAnimationFrame(animationFrameId);
|
|
708
|
+
animationFrameId = null;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
playButton.addEventListener("click", () => {
|
|
713
|
+
if (isPlaying) {
|
|
714
|
+
stopAnimation();
|
|
715
|
+
} else {
|
|
716
|
+
startAnimation();
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
if (statusEl) {
|
|
721
|
+
statusEl.textContent = "Ready";
|
|
722
|
+
}
|
|
658
723
|
});
|
|
659
724
|
} catch (error) {
|
|
660
725
|
console.error(error);
|
|
661
|
-
if (statusEl)
|
|
726
|
+
if (statusEl) {
|
|
727
|
+
statusEl.textContent = "Something went wrong";
|
|
728
|
+
}
|
|
662
729
|
hasBootstrapped = false;
|
|
663
730
|
}
|
|
664
731
|
}
|