Skip to content
Open
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
3 changes: 1 addition & 2 deletions packages/turf-great-circle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ then a `LineString` will be returned with duplicate coordinates the length of th

* `options.properties` **[Object][4]** line feature properties (optional, default `{}`)
* `options.npoints` **[number][5]** number of points (optional, default `100`)
* `options.offset` **[number][5]** offset controls the likelyhood that lines will
be split which cross the dateline. The higher the number the more likely. (optional, default `10`)
* `options.offset` **[number][5]?** NOTE: deprecated: Antimeridian splitting is now automatic and this option has no effect

### Examples

Expand Down
7 changes: 3 additions & 4 deletions packages/turf-great-circle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import { GreatCircle } from "arc";
* @param {Object} [options={}] Optional parameters
* @param {Object} [options.properties={}] line feature properties
* @param {number} [options.npoints=100] number of points
* @param {number} [options.offset=10] offset controls the likelyhood that lines will
* be split which cross the dateline. The higher the number the more likely.
* @param {number} [options.offset] NOTE: deprecated: Antimeridian splitting is now automatic and this option has no effect
* @returns {Feature<LineString | MultiLineString>} great circle line feature
* @example
* var start = turf.point([-122, 48]);
Expand All @@ -45,7 +44,7 @@ function greatCircle(
): Feature<LineString | MultiLineString> {
// Optional parameters
if (typeof options !== "object") throw new Error("options is invalid");
const { properties = {}, npoints = 100, offset = 10 } = options;
const { properties = {}, npoints = 100 } = options;

const startCoord = getCoord(start);
const endCoord = getCoord(end);
Expand All @@ -61,7 +60,7 @@ function greatCircle(
properties || {}
);

const line = generator.Arc(npoints, { offset: offset });
const line = generator.Arc(npoints);

return line.json() as Feature<
LineString | MultiLineString,
Expand Down
2 changes: 1 addition & 1 deletion packages/turf-great-circle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"@turf/helpers": "workspace:*",
"@turf/invariant": "workspace:*",
"@types/geojson": "catalog:",
"arc": "^0.2.0",
"arc": "^1.0.0",
"tslib": "catalog:"
}
}
68 changes: 56 additions & 12 deletions packages/turf-great-circle/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { writeJsonFileSync } from "write-json-file";
import type {
FeatureCollection,
LineString,
MultiLineString,
Feature,
Geometry,
Point,
Expand Down Expand Up @@ -40,6 +41,12 @@ function getStartEndPoints(fixture: (typeof fixtures)[0]) {
return { start, end };
}

function getFixture(name: string) {
const fixture = fixtures.find((f) => f.name === name);
if (!fixture) throw new Error(`fixture not found: ${name}`);
return fixture;
}

test("turf-great-circle", (t) => {
fixtures.forEach((fixture) => {
const name = fixture.name;
Expand Down Expand Up @@ -77,7 +84,7 @@ test("turf-great-circle with same input and output", (t) => {
});

test("turf-great-circle accepts Feature<Point> inputs", (t) => {
const { start, end } = getStartEndPoints(fixtures[0]);
const { start, end } = getStartEndPoints(getFixture("basic"));
t.doesNotThrow(
() => greatCircle(start, end),
"accepts Feature<Point> inputs"
Expand All @@ -86,7 +93,7 @@ test("turf-great-circle accepts Feature<Point> inputs", (t) => {
});

test("turf-great-circle accepts Point geometry inputs", (t) => {
const { start, end } = getStartEndPoints(fixtures[0]);
const { start, end } = getStartEndPoints(getFixture("basic"));
t.doesNotThrow(
() => greatCircle(start.geometry, end.geometry),
"accepts Point geometry inputs"
Expand All @@ -95,7 +102,7 @@ test("turf-great-circle accepts Point geometry inputs", (t) => {
});

test("turf-great-circle accepts Position inputs", (t) => {
const { start, end } = getStartEndPoints(fixtures[0]);
const { start, end } = getStartEndPoints(getFixture("basic"));
t.doesNotThrow(
() => greatCircle(start.geometry.coordinates, end.geometry.coordinates),
"accepts Position inputs"
Expand All @@ -104,7 +111,7 @@ test("turf-great-circle accepts Position inputs", (t) => {
});

test("turf-great-circle applies custom properties", (t) => {
const { start, end } = getStartEndPoints(fixtures[0]);
const { start, end } = getStartEndPoints(getFixture("basic"));
const withProperties = greatCircle(start, end, {
properties: { name: "Test Route" },
});
Expand All @@ -117,7 +124,7 @@ test("turf-great-circle applies custom properties", (t) => {
});

test("turf-great-circle respects npoints option", (t) => {
const { start, end } = getStartEndPoints(fixtures[0]);
const { start, end } = getStartEndPoints(getFixture("basic"));
const withCustomPoints = greatCircle(start, end, { npoints: 5 });
t.equal(
(withCustomPoints.geometry as LineString).coordinates.length,
Expand All @@ -127,13 +134,12 @@ test("turf-great-circle respects npoints option", (t) => {
t.end();
});

test("turf-great-circle respects offset and npoints options", (t) => {
const { start, end } = getStartEndPoints(fixtures[0]);
const withOffset = greatCircle(start, end, { offset: 100, npoints: 10 });
t.equal(
(withOffset.geometry as LineString).coordinates.length,
10,
"respects offset and npoints options"
test("turf-great-circle offset option is accepted but has no effect", (t) => {
const { start, end } = getStartEndPoints(getFixture("basic"));
// NOTE: offset is deprecated and a no-op since arc@1.0.0; antimeridian splitting is automatic with a MultiLineString result when needed. This test ensures that the option is accepted without throwing, but does not verify any behavior since it has no effect.
t.doesNotThrow(
() => greatCircle(start, end, { offset: 100, npoints: 10 }),
"accepts offset option without throwing"
);
t.end();
});
Expand All @@ -150,3 +156,41 @@ test("turf-great-circle with antipodal start and end", (t) => {

t.end();
});

// Antimeridian crossing fixtures. Each route crosses the international dateline and must produce a MultiLineString with segments that close and open exactly at ±180°.
[
"antimeridian-tokyo-lax",
"antimeridian-auckland-lax",
"antimeridian-shanghai-sfo",
].forEach((fixtureName) => {
test(`turf-great-circle antimeridian split: ${fixtureName}`, (t) => {
const { start, end } = getStartEndPoints(getFixture(fixtureName));
// NOTE: npoints=10 reproduces the original bug https://github.com/Turfjs/turf/issues/3030 (also confirmed by @jgravois)
const result = greatCircle(start, end, { npoints: 10 });
const geom = result.geometry as MultiLineString;

t.equal(
geom.type,
"MultiLineString",
"produces MultiLineString across antimeridian"
);

const lastOfFirst = geom.coordinates[0].at(-1)!;
const firstOfSecond = geom.coordinates[1][0];

t.ok(
Math.abs(Math.abs(lastOfFirst[0]) - 180) < 0.001,
"first segment ends at ±180°"
);
t.ok(
Math.abs(Math.abs(firstOfSecond[0]) - 180) < 0.001,
"second segment starts at ±180°"
);
t.ok(
Math.abs(lastOfFirst[1] - firstOfSecond[1]) < 0.001,
"latitude matches at antimeridian split"
);

t.end();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": { "name": "Auckland" },
"geometry": {
"type": "Point",
"coordinates": [174.79, -36.85]
}
},
{
"type": "Feature",
"properties": { "name": "Los Angeles" },
"geometry": {
"type": "Point",
"coordinates": [-118.41, 33.94]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": { "name": "Shanghai" },
"geometry": {
"type": "Point",
"coordinates": [121.81, 31.14]
}
},
{
"type": "Feature",
"properties": { "name": "San Francisco" },
"geometry": {
"type": "Point",
"coordinates": [-122.38, 37.62]
}
}
]
}
21 changes: 21 additions & 0 deletions packages/turf-great-circle/test/in/antimeridian-tokyo-lax.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": { "name": "Tokyo" },
"geometry": {
"type": "Point",
"coordinates": [139.7798, 35.5494]
}
},
{
"type": "Feature",
"properties": { "name": "Los Angeles" },
"geometry": {
"type": "Point",
"coordinates": [-118.4085, 33.9416]
}
}
]
}
140 changes: 140 additions & 0 deletions packages/turf-great-circle/test/out/antimeridian-auckland-lax.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "MultiLineString",
"coordinates": [
[
[174.79, -36.85],
[175.691979, -36.23329],
[176.579765, -35.609916],
[177.453753, -34.980132],
[178.314335, -34.344181],
[179.161902, -33.7023],
[179.996841, -33.054717],
[180, -33.052238]
],
[
[-180, -33.052238],
[-179.180464, -32.401654],
[-178.369634, -31.743323],
[-177.570293, -31.079931],
[-176.782073, -30.411677],
[-176.004608, -29.738754],
[-175.237542, -29.061348],
[-174.480521, -28.379638],
[-173.7332, -27.693799],
[-172.995237, -27.003999],
[-172.266299, -26.310402],
[-171.546059, -25.613163],
[-170.834194, -24.912436],
[-170.13039, -24.208369],
[-169.434337, -23.501103],
[-168.745734, -22.790779],
[-168.064281, -22.07753],
[-167.38969, -21.361486],
[-166.721673, -20.642774],
[-166.059951, -19.921517],
[-165.404251, -19.197834],
[-164.754301, -18.471841],
[-164.109839, -17.743652],
[-163.470605, -17.013375],
[-162.836345, -16.281118],
[-162.206808, -15.546986],
[-161.581749, -14.811081],
[-160.960926, -14.073502],
[-160.344102, -13.334346],
[-159.731042, -12.593709],
[-159.121517, -11.851685],
[-158.515298, -11.108364],
[-157.912163, -10.363837],
[-157.31189, -9.618192],
[-156.714261, -8.871515],
[-156.119059, -8.123892],
[-155.526072, -7.375407],
[-154.935089, -6.626144],
[-154.345899, -5.876184],
[-153.758295, -5.125609],
[-153.17207, -4.374499],
[-152.587021, -3.622933],
[-152.002943, -2.870992],
[-151.419634, -2.118753],
[-150.836891, -1.366295],
[-150.254514, -0.613695],
[-149.6723, 0.138967],
[-149.090048, 0.891616],
[-148.507559, 1.644172],
[-147.924631, 2.396558],
[-147.341061, 3.148697],
[-146.756648, 3.900508],
[-146.171189, 4.651915],
[-145.584479, 5.402837],
[-144.996314, 6.153194],
[-144.406486, 6.902906],
[-143.814787, 7.651892],
[-143.221007, 8.400068],
[-142.624934, 9.147351],
[-142.026353, 9.893657],
[-141.425049, 10.6389],
[-140.820801, 11.382992],
[-140.213388, 12.125845],
[-139.602584, 12.867367],
[-138.988161, 13.607468],
[-138.369888, 14.346053],
[-137.747528, 15.083025],
[-137.120844, 15.818288],
[-136.489592, 16.551739],
[-135.853525, 17.283277],
[-135.21239, 18.012796],
[-134.565932, 18.740187],
[-133.91389, 19.46534],
[-133.255998, 20.188141],
[-132.591984, 20.908472],
[-131.921572, 21.626213],
[-131.24448, 22.34124],
[-130.560422, 23.053425],
[-129.869102, 23.762635],
[-129.170223, 24.468736],
[-128.463479, 25.171587],
[-127.748558, 25.871043],
[-127.025144, 26.566955],
[-126.292912, 27.259169],
[-125.551532, 27.947526],
[-124.800669, 28.63186],
[-124.039979, 29.312001],
[-123.269115, 29.987773],
[-122.487721, 30.658994],
[-121.695437, 31.325475],
[-120.891897, 31.987022],
[-120.076729, 32.643432],
[-119.249557, 33.294497],
[-118.41, 33.94]
]
]
},
"properties": {}
},
{
"type": "Feature",
"properties": {
"name": "Auckland"
},
"geometry": {
"type": "Point",
"coordinates": [174.79, -36.85]
}
},
{
"type": "Feature",
"properties": {
"name": "Los Angeles"
},
"geometry": {
"type": "Point",
"coordinates": [-118.41, 33.94]
}
}
]
}
Loading
Loading