dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Geodesic math toolbox for DeepSeek Harness (dsh): great-circle distance, bearings, destination points, DMS parsing, rhumb lines, path intersections, spherical polygon areas — zero runtime dependencies
Geodesic math toolbox for DeepSeek Harness (dsh): great-circle distances, bearings, destination points, DMS parsing, rhumb lines, path intersections, spherical polygon areas, off-track (cross-track) distances, point-in-polygon tests, multi-leg route totals and spherical caps (coverage areas) — all as local, deterministic tools with zero runtime dependencies.
中文简介:dsh 插件「大地测量工具箱」——大圆距离、方位角、目的地推算、DMS 坐标解析、恒向线(罗盘等角航线)距离、两条大圆路径交点、球面多边形面积、点到路径的偏航距离(cross-track)、点是否在球面多边形内、多段航线总里程与偏航/绕行量、球面圆(覆盖范围),共十一个纯计算工具,零运行时依赖。专治大模型手算球面几何的常见错误(弧度与角度混用、经纬度顺序颠倒、忘记地球半径、把球面多边形当平面算、反算偏航距离时把「最近点」与「垂足」混为一谈、把球面覆盖范围当平面圆算等)。
LLMs reliably fumble geodesic math: they mix up latitude/longitude order, use the wrong Earth radius, forget degrees→radians, botch DMS conversions, and treat spherical polygons as planar. This plugin moves that math out of the model's head and into exact spherical trigonometry:
geo_distance — great-circle distance (haversine) between two points, with initial/final bearings and the 16-wind compass direction; results in km, m, mi, or nmigeo_bearing — initial & final great-circle bearings plus the great-circle midpoint of two pointsgeo_destination — the direct geodesic problem: given a start point, bearing and distance, compute the destination (with a round-trip distance check)coord_parse — parse and validate coordinate strings (decimal, DMS, suffixes, pairs) and convert them to decimal degrees + formatted DMSgeo_rhumb — rhumb-line (constant-heading / loxodrome) distance and bearing between two points, with the great-circle distance for comparisongeo_intersection — where two great-circle paths (start point + initial bearing each) cross; reports the nearest crossing, the antipodal crossing, and distances from both startsgeo_area — area and perimeter of a spherical polygon via spherical excess (Girard's theorem), with the complementary region's area always includedgeo_cross_track — how far a point is off a great-circle path: signed cross-track distance, along-track distance to the perpendicular foot, the foot's coordinates, whether the foot is between the path endpoints, and the distance to the nearest point of the segmentgeo_point_in_polygon — is a coordinate inside a spherical polygon? Reports both sides of the boundary (the enclosed region and the region left of the directed edges), boundary hits, and the area of the region the point falls ingeo_path — totals a multi-leg route: each leg's length, heading (bearing + compass point) and running total, plus the route total, the straight-line distance between the endpoints, the detour and the distance back to the startgeo_circle — a spherical cap ("everything within r of a point"): its area and share of the sphere, its boundary circle's length, whether that boundary is a great circle, the latitude/longitude box it occupies (pole and antimeridian cases included), and whether a given point is inside itAll math uses the IUGG mean Earth radius 6371.0088 km (configurable via the plugin radiusKm option, e.g. for lunar or planetary calculations).
dsh plugin --profile web add github:TYEclipse/dsh-geodesy
Requires pnpm on your PATH. Then restart the profile's services (or dsh restart web).
Ask the agent questions that involve Earth-surface math; it will call the tools automatically:
geo_rhumb — (45°N, 0°) → (45°N, 90°) at constant heading:
{
"valid": true,
"unit": "km",
"distance": 7076.411574,
"kilometers": 7076.411574,
"miles": 4397.078297,
"nauticalMiles": 3820.956573,
"bearing": 90,
"compass": "E",
"greatCircleKm": 6671.704814
}
geo_intersection — equator (east from (0,0)) × meridian (north from (0,90)):
{
"valid": true,
"lat": 0,
"lon": 90,
"distanceFromStart1Km": 10007.557221,
"distanceFromStart2Km": 0,
"antipodeLat": 0,
"antipodeLon": -90,
"antipodeDistanceFromStart1Km": 10007.557221,
"antipodeDistanceFromStart2Km": 20015.114442
}
geo_area — the octant triangle (0,0), (0,90), (90,0): exactly one eighth of the sphere:
{
"valid": true,
"vertices": 3,
"areaKm2": 63758235.121609,
"perimeterKm": 30022.671663,
"excessRadians": 1.5707963268,
"complementKm2": 446307645.851263
}
geo_cross_track — the query point (1, 5) against the equator path (0,0) → (0,10):
{
"valid": true,
"side": "left",
"crossTrackKm": 111.19508,
"distanceToPathKm": 111.19508,
"alongTrackKm": 555.975401,
"footLat": 0,
"footLon": 5,
"withinSegment": true,
"distanceToSegmentKm": 111.19508,
"pathLengthKm": 1111.950802,
"trackBearing": 90,
"bearingToFoot": 180
}
geo_point_in_polygon — (45, 45) against the octant triangle (0,0), (0,90), (90,0):
{
"valid": true,
"insideEnclosed": true,
"insideLeftRegion": true,
"onBoundary": false,
"vertices": 3,
"areaKm2": 63758235.121609,
"complementKm2": 446307645.851263,
"regionAreaKm2": 63758235.121609
}
geo_path — London (51.5074, -0.1278) → Paris (48.8566, 2.3522) → Rome (41.9028, 12.4964): the route is 15 km longer than the direct line, a detour of about 1%:
{
"valid": true,
"points": 3,
"legs": [
{ "leg": 1, "fromIndex": 0, "toIndex": 1, "distanceKm": 343.556535, "cumulativeKm": 343.556535, "initialBearing": 148.115617, "compass": "SSE" },
{ "leg": 2, "fromIndex": 1, "toIndex": 2, "distanceKm": 1105.281675, "cumulativeKm": 1448.83821, "initialBearing": 130.58791, "compass": "SE" }
],
"totalKm": 1448.83821,
"straightKm": 1433.783288,
"detourKm": 15.054922,
"detourPercent": 1.05,
"closureKm": 1433.783288,
"closesLoop": false,
"maxLegKm": 1105.281675,
"minLegKm": 343.556535,
"zeroLengthLegs": []
}
geo_circle — everything within 300 km of Beijing (39.9042, 116.4074), asking whether (40.5, 117) is inside. The cap is 0.055% of the Earth's surface, its boundary circle is 1884 km long, and the box it occupies spans 5.4° of latitude and 7.0° of longitude:
{
"valid": true,
"angularRadiusDeg": 2.697961,
"areaKm2": 282691.098586,
"surfaceFraction": 0.0005542247,
"boundaryKm": 1884.259081,
"boundaryIsGreatCircle": false,
"minLat": 37.206239,
"maxLat": 42.602161,
"minLon": 112.889481,
"maxLon": 119.925319,
"lonSpanDeg": 7.035838,
"crossesAntimeridian": false,
"coversAllLongitudes": false,
"includesNorthPole": false,
"includesSouthPole": false,
"coversWholeSphere": false,
"pointDistanceKm": 83.198018,
"containsPoint": true,
"pointOnBoundary": false
}
geo_distance — Beijing (39.9042, 116.4074) → Shanghai (31.2304, 121.4737):
{
"valid": true,
"unit": "km",
"distance": 1067.311645,
"kilometers": 1067.311645,
"miles": 663.196709,
"nauticalMiles": 576.302184,
"initialBearing": 153.072675,
"finalBearing": 156.029266,
"compass": "SSE"
}
geo_area winding: the reported area is the region to the LEFT of the directed edges. Counter-clockwise vertex order (seen from outside the sphere) yields the polygon itself; clockwise order yields its complement. complementKm2 is always included, so either region is one field away. The polygon must be simple (non-self-intersecting); at most 100 vertices.geo_point_in_polygon reports both sides of the boundary, because a boundary on a sphere always has two:
insideEnclosed — the ordinary "is the point inside the polygon". The enclosed region is taken to be the smaller of the two regions the boundary splits the sphere into, so a clockwise-wound square still means its interior.insideLeftRegion — the region to the LEFT of the directed edges, i.e. exactly the region geo_area calls areaKm2. With counter-clockwise vertex order the two answers agree; with clockwise order they are opposites (a note says so).onBoundary: true and in neither region. The method is a signed great-circle ray-crossing winding, which — unlike the popular "sum the angles at the query point" shortcut — does not report points near a polygon's antipode as inside.geo_cross_track sign conventions: crossTrackKm is positive when the query point is to the LEFT of the direction of travel (right-hand rule, seen from outside the sphere); alongTrackKm is positive when the perpendicular foot lies ahead of the path start and negative when it lies behind. When the foot falls outside the segment, withinSegment: false and distanceToSegmentKm is the distance to the nearer endpoint — not the perpendicular distance (a note says so). A point exactly 90° off the path has no unique foot and is reported as an error.geo_path: the legs are great-circle arcs, so the route total is the sum of the leg lengths and the detour (detourKm = total − straight line) is never negative — it is zero only when every waypoint lies on the single great circle through the endpoints. detourKm is the difference of the two reported totals (each rounded to 6 decimals), so it can be checked by hand against them. A leg joining two identical waypoints is reported as 0 km with no initialBearing/compass (a zero-length great circle has no heading — inventing 0° would be a silent wrong answer) and its leg number is listed in zeroLengthLegs. When the last waypoint equals the first, closesLoop is true, the straight-line distance is 0 and no detourPercent is reported.geo_circle describes a spherical cap, not a flat disc:
areaKm2 is the exact cap area 2πR²(1 − cos d) and surfaceFraction its share of the whole sphere; the planar approximation πr² only holds for tiny caps (at r = 1000 km it overshoots the true area by 0.21%).centre ± d; boundaryIsGreatCircle is true only when the radius is exactly a quarter of the circumference (d = 90°), where the cap is a hemisphere.includesNorthPole/includesSouthPole are set, coversAllLongitudes is true, and minLon/maxLon are reported as the full range (the cap is not a rectangle). A cap that straddles the antimeridian has a box that wraps: crossesAntimeridian is true and minLon is east of maxLon (join them through ±180°); lonSpanDeg always holds the true angular width.coversWholeSphere, area = 4πR², boundaryKm = 0).containsPoint/pointOnBoundary are decided by great-circle distance, never by the box — a box test would misjudge points near the box corners. "On the boundary" means within ~6 mm of the circle; a point placed there counts as neither inside nor outside. Note that tool coordinates are rounded to 1e-6 degrees (~11 cm of arc), which is coarser than that tolerance, so do not expect a rounded coordinate to be recognized as exactly on the edge.geo_intersection reports the crossing nearest to the first path's start; when that crossing lies on the reverse side of path 1 (bearing from start 1 differs by ~180°), a note says so and the antipodal crossing is the one ahead.geo_rhumb returns bearing 0 for zero-length lines (no unique heading exists).coord_parse accepts, deterministically:
| Input | Meaning |
|---|---|
39.9042, 116.4074 | decimal pair, lat, lon order |
116.4074E, 39.9042N | pair with suffixes — order is irrelevant, axes come from suffixes |
39°54′15″N 116°23′27″E | DMS pair (comma optional when suffixed) |
37°46′30″N / 37d46m30sN / 37:46:30N / 37 46 30 N | single DMS coordinate |
37.775N / -122.419 | single decimal (negative = S/W) |
37.775, kind: "lat" | unsuffixed single value with an explicit axis hint |
Invalid input returns valid: false with a reason — never a silent guess.
| Tool | Purpose |
|---|---|
geo_distance | haversine distance, km/m/mi/nmi, initial & final bearing, compass point |
geo_bearing | initial/final bearings + great-circle midpoint |
geo_destination | destination point from start + bearing + distance, with round-trip check |
coord_parse | parse/validate decimal & DMS coordinate strings → decimal degrees + DMS |
geo_rhumb | constant-heading distance + bearing (loxodrome), great-circle comparison |
geo_intersection | crossing of two great-circle paths (+ antipodal crossing, distances from starts) |
geo_area | spherical polygon area + perimeter (spherical excess), complement area |
geo_cross_track | off-track distance to a great-circle path: signed cross-track + along-track, foot, segment distance |
geo_point_in_polygon | inside/outside test for a spherical polygon (both sides, boundary hits, region area) |
geo_path | multi-leg route totals: per-leg distance/heading/running total, detour vs the straight line |
geo_circle | spherical cap: area, boundary circle length, lat/lon box (poles & antimeridian), containment |
plugins:
dsh-geodesy:
radiusKm: 6371.0088 # sphere radius; use 1737.4 for the Moon, 3389.5 for Mars
pnpm install
pnpm build
pnpm test # 223 tests, anchored to independently computed closed-form values (test/oracle/anchors.py)
pnpm lint
MIT © TYEclipse