Skip to content

Commit e37fce1

Browse files
fix: GeoJSON export (#1283)
* fix: use global vars instead of window. * feat: add GitHub Actions workflow for unit tests * fix: change mapCoordinates declaration from let to var for compatibility
1 parent 29bc283 commit e37fce1

5 files changed

Lines changed: 124 additions & 5 deletions

File tree

.github/workflows/unit-tests.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Unit Tests
2+
on:
3+
pull_request:
4+
branches: [ master ]
5+
jobs:
6+
test:
7+
timeout-minutes: 60
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v5
11+
- uses: actions/setup-node@v5
12+
with:
13+
node-version: '24'
14+
- name: Install dependencies
15+
run: npm ci
16+
- name: Run Unit tests
17+
run: npm run test

public/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ const onZoom = debounce(function () {
187187
}, 50);
188188
const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", onZoom);
189189

190-
let mapCoordinates = {}; // map coordinates on globe
190+
var mapCoordinates = {}; // map coordinates on globe
191191
let populationRate = +byId("populationRateInput").value;
192192
let distanceScale = +byId("distanceScaleInput").value;
193193
let urbanization = +byId("urbanizationInput").value;

src/utils/commonUtils.test.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { expect, describe, it } from 'vitest'
2+
import { getLongitude, getLatitude, getCoordinates } from './commonUtils'
3+
4+
describe('getLongitude', () => {
5+
const mapCoordinates = { lonW: -10, lonT: 20 };
6+
const graphWidth = 1000;
7+
8+
it('should calculate longitude at the left edge (x=0)', () => {
9+
expect(getLongitude(0, mapCoordinates, graphWidth, 2)).toBe(-10);
10+
});
11+
12+
it('should calculate longitude at the right edge (x=graphWidth)', () => {
13+
expect(getLongitude(1000, mapCoordinates, graphWidth, 2)).toBe(10);
14+
});
15+
16+
it('should calculate longitude at the center (x=graphWidth/2)', () => {
17+
expect(getLongitude(500, mapCoordinates, graphWidth, 2)).toBe(0);
18+
});
19+
20+
it('should respect decimal precision', () => {
21+
// 333/1000 * 20 = 6.66, -10 + 6.66 = -3.34
22+
expect(getLongitude(333, mapCoordinates, graphWidth, 4)).toBe(-3.34);
23+
});
24+
25+
it('should handle different map coordinate ranges', () => {
26+
const wideMap = { lonW: -180, lonT: 360 };
27+
expect(getLongitude(500, wideMap, graphWidth, 2)).toBe(0);
28+
expect(getLongitude(0, wideMap, graphWidth, 2)).toBe(-180);
29+
expect(getLongitude(1000, wideMap, graphWidth, 2)).toBe(180);
30+
});
31+
});
32+
33+
describe('getLatitude', () => {
34+
const mapCoordinates = { latN: 60, latT: 40 };
35+
const graphHeight = 800;
36+
37+
it('should calculate latitude at the top edge (y=0)', () => {
38+
expect(getLatitude(0, mapCoordinates, graphHeight, 2)).toBe(60);
39+
});
40+
41+
it('should calculate latitude at the bottom edge (y=graphHeight)', () => {
42+
expect(getLatitude(800, mapCoordinates, graphHeight, 2)).toBe(20);
43+
});
44+
45+
it('should calculate latitude at the center (y=graphHeight/2)', () => {
46+
expect(getLatitude(400, mapCoordinates, graphHeight, 2)).toBe(40);
47+
});
48+
49+
it('should respect decimal precision', () => {
50+
// 60 - (333/800 * 40) = 60 - 16.65 = 43.35
51+
expect(getLatitude(333, mapCoordinates, graphHeight, 4)).toBe(43.35);
52+
});
53+
54+
it('should handle equator-centered maps', () => {
55+
const equatorMap = { latN: 45, latT: 90 };
56+
expect(getLatitude(400, equatorMap, graphHeight, 2)).toBe(0);
57+
});
58+
});
59+
60+
describe('getCoordinates', () => {
61+
const mapCoordinates = { lonW: -10, lonT: 20, latN: 60, latT: 40 };
62+
const graphWidth = 1000;
63+
const graphHeight = 800;
64+
65+
it('should return [longitude, latitude] tuple', () => {
66+
const result = getCoordinates(500, 400, mapCoordinates, graphWidth, graphHeight, 2);
67+
expect(result).toEqual([0, 40]);
68+
});
69+
70+
it('should calculate coordinates at top-left corner', () => {
71+
const result = getCoordinates(0, 0, mapCoordinates, graphWidth, graphHeight, 2);
72+
expect(result).toEqual([-10, 60]);
73+
});
74+
75+
it('should calculate coordinates at bottom-right corner', () => {
76+
const result = getCoordinates(1000, 800, mapCoordinates, graphWidth, graphHeight, 2);
77+
expect(result).toEqual([10, 20]);
78+
});
79+
80+
it('should respect decimal precision for both coordinates', () => {
81+
const result = getCoordinates(333, 333, mapCoordinates, graphWidth, graphHeight, 4);
82+
expect(result[0]).toBe(-3.34); // longitude
83+
expect(result[1]).toBe(43.35); // latitude
84+
});
85+
86+
it('should use default precision of 2 decimals', () => {
87+
const result = getCoordinates(333, 333, mapCoordinates, graphWidth, graphHeight);
88+
expect(result[0]).toBe(-3.34);
89+
expect(result[1]).toBe(43.35);
90+
});
91+
92+
it('should handle global map coordinates', () => {
93+
const globalMap = { lonW: -180, lonT: 360, latN: 90, latT: 180 };
94+
const result = getCoordinates(500, 400, globalMap, graphWidth, graphHeight, 2);
95+
expect(result).toEqual([0, 0]); // center of the world
96+
});
97+
});

src/utils/commonUtils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,4 +317,9 @@ declare global {
317317
getLatitude: typeof getLatitude;
318318
getCoordinates: typeof getCoordinates;
319319
}
320+
321+
// Global variables defined in main.js
322+
var mapCoordinates: { latT?: number; latN?: number; latS?: number; lonT?: number; lonW?: number; lonE?: number };
323+
var graphWidth: number;
324+
var graphHeight: number;
320325
}

src/utils/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ window.isLand = (i: number) => isLand(i, (window as any).pack);
116116
window.isWater = (i: number) => isWater(i, (window as any).pack);
117117

118118
import { clipPoly, getSegmentId, debounce, throttle, parseError, getBase64, openURL, wiki, link, isCtrlClick, generateDate, getLongitude, getLatitude, getCoordinates, initializePrompt } from "./commonUtils";
119-
window.clipPoly = (points: [number, number][], secure?: number) => clipPoly(points, (window as any).graphWidth, (window as any).graphHeight, secure);
119+
window.clipPoly = (points: [number, number][], secure?: number) => clipPoly(points, graphWidth, graphHeight, secure);
120120
window.getSegmentId = getSegmentId;
121121
window.debounce = debounce;
122122
window.throttle = throttle;
@@ -127,9 +127,9 @@ window.wiki = wiki;
127127
window.link = link;
128128
window.isCtrlClick = isCtrlClick;
129129
window.generateDate = generateDate;
130-
window.getLongitude = (x: number, decimals?: number) => getLongitude(x, (window as any).mapCoordinates, (window as any).graphWidth, decimals);
131-
window.getLatitude = (y: number, decimals?: number) => getLatitude(y, (window as any).mapCoordinates, (window as any).graphHeight, decimals);
132-
window.getCoordinates = (x: number, y: number, decimals?: number) => getCoordinates(x, y, (window as any).mapCoordinates, (window as any).graphWidth, (window as any).graphHeight, decimals);
130+
window.getLongitude = (x: number, decimals?: number) => getLongitude(x, mapCoordinates, graphWidth, decimals);
131+
window.getLatitude = (y: number, decimals?: number) => getLatitude(y, mapCoordinates, graphHeight, decimals);
132+
window.getCoordinates = (x: number, y: number, decimals?: number) => getCoordinates(x, y, mapCoordinates, graphWidth, graphHeight, decimals);
133133

134134
// Initialize prompt when DOM is ready
135135
if (document.readyState === 'loading') {

0 commit comments

Comments
 (0)