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 "../../../src/index.js";
9
- import { flattenPositions } from "../../../src/adapters/shared/geometry.js";
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.textContent = "Loading data…";
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
- map.setFeatureState(
562
- { source: morphControls.sourceIds.interpolated, id: hoveredStateId },
563
- { hover: true }
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
- slider.addEventListener("input", (event) => {
639
- const value = Number(event.target.value);
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
- statusEl.textContent = "Ready";
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) statusEl.textContent = "Something went wrong";
726
+ if (statusEl) {
727
+ statusEl.textContent = "Something went wrong";
728
+ }
662
729
  hasBootstrapped = false;
663
730
  }
664
731
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geo-morpher",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "GeoJSON morphing utilities with MapLibre-first adapter and Leaflet compatibility",
5
5
  "type": "module",
6
6
  "main": "src/index.js",