
Searoute
io.github.mayurrawte由 GitHub 账号 mayurrawte 发布v0.1.1更新于 Nov 3, 2026
Real shortest sea routes between ports: distance, canals/straits, GeoJSON. UN/LOCODE aware.
概览
计算两点或港口之间真实的最短海上航线,返回距离、途经运河与海峡以及 GeoJSON 几何。
- 可以做什么
- 计算两点或 UN/LOCODE 港口代码之间的最短海上航线,含距离、时长与途经海峡
- 返回备选航线,例如绕开苏伊士或巴拿马,以及多段港口挂靠路线
- 以 GeoJSON 线串形式给出航线几何,便于地图渲染
- 适用场景
- 适合需要回答海运距离、ETA 或航线问题的场景,例如上海到鹿特丹的海运距离,或避开某条运河后的路线走向,而不是让模型凭大圆距离猜测。
- 运行要求
- 通过 npm 包 @searoute-ts/mcp 以 stdio 在本地运行,通常用 npx 启动,因此需要 Node.js。未声明任何账号、API 密钥、环境变量或请求头。内置海运网络可离线使用;从网址加载网络或港口数据集属可选项,需要网络访问。
安装
在 SourceWeft 中
- 打开 控制台中的 Searoute,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
searoute-ts
Shortest sea route between any two points on Earth. A TypeScript / JavaScript library for maritime route planning, port-to-port distance, ETA estimation, and shipping-lane visualisation — powered by the 2025 Eurostat maritime network.
[npm version] [npm downloads] [CI] [license] [types]
🗺️ Try the interactive demo — click two points on a map and see the route, with all options live. (source)
📏 Port-to-port sea distances — distance, sailing time and canal passages for common routes between major ports.
Works from plain JavaScript too — the package ships compiled
.jsplus.d.tsdeclarations. The-tsin the name is for searchability, not a language requirement.
Why searoute-ts
- 🚢 Realistic shipping routes, not great-circle lines through Eurasia.
- 🗺️ Returns GeoJSON — drop straight into Leaflet, Mapbox, deck.gl, MapLibre.
- 🌊 2025 Eurostat marnet with explicit Suez, Panama, Bab-el-Mandeb, Malacca, Gibraltar, Dover, Kiel, Corinth, Bering, Magellan, NW/NE Passage labels.
- 🚫 Canal & strait restrictions — force Cape of Good Hope during a Red Sea disruption with one option.
- 📦 Vessel-draft gating — auto-block Panama (15.2 m), Suez (20.1 m), Kiel (7 m), Corinth (7.3 m) when the vessel exceeds the canal limit.
- 🛤️ K-shortest alternatives —
seaRouteAlternativesreturns the baseline plus up to N realistic alternatives. - 🧭 Multi-leg waypoints —
seaRouteMultifor port rotations and itineraries. - ⏱️ ETA from speed —
speedKnots→durationHours. - 🛠️ Modern toolchain — TypeScript 5, ESM + CJS dual build, types included, Node 18+, zero peer deps.
Quick examples
Basic — shortest route
With ETA and units
Red Sea / Suez disruption — force Cape of Good Hope
Vessel-aware — Ultra Large Container Ship
Port codes (UN/LOCODE)
The ~1 600-port dataset lives behind the searoute-ts/ports subpath so the core
stays lean — importing it registers the resolver. You can also resolve codes
yourself:
Unknown codes throw UnknownPortError. See Port codes below for provenance.
Load the port dataset from a CDN instead of bundling it
Don't want to bundle the ~135 KB dataset? Fetch it at runtime with loadPorts —
the analog of loadNetwork.
The dataset also ships as a raw dist/ports.json, so jsDelivr/unpkg serve it
versioned for free:
(dist/ports.json ships from the release that adds port codes onward — pin any
version at or after it for reproducibility.)
loadPorts registers the fetched dataset (so code strings resolve) and returns
it. It uses the global fetch (Node ≥18 / browsers); pass { fetch } to override.
Multi-leg / port rotation
Alternative routes (Yen-style canal permutation)
Fetch the network from a URL instead of bundling it (optional)
The network is bundled by default, so seaRoute works offline with zero setup.
If you'd rather not ship the ~1 MB network (e.g. to trim a browser bundle,
or to use an updated network without upgrading the package), fetch it at
runtime and pass it via the existing network option:
Only the fetch is async — seaRoute itself stays synchronous. loadNetwork
uses the global fetch (Node ≥18 and all browsers); pass { fetch } to supply
your own. This is purely opt-in; nothing changes if you don't use it.
Which option should I use?
Versioning the hosted network
You choose the version by choosing the URL:
-
@latest/ rolling — the GitHub Pages URL above always serves the current network. Convenient, but it can change under you. -
Pinned & immutable — because the package is on npm, jsDelivr and unpkg serve every published version automatically, with immutable per-version URLs:
A pinned URL never changes, so your routes stay reproducible. (These standalone-JSON CDN paths land with the package once the network ships as a separate asset — see issue #10; until then, use the GitHub Pages URL.)
For production, prefer a pinned URL (or just the bundled default) so your distances don't shift when the network is updated.
Higher-resolution networks (optional)
The bundled network is Eurostat's 100 km marnet_plus. Eurostat also
publishes finer resolutions, which give more accurate coastal routing and
shorter-hop fidelity at the cost of a larger download and slightly slower
first-route graph construction. Two moderate resolutions ship as subpath
exports so you only pay for them if you import them:
Like the bundled default, each variant ships once as a shared
dist/data/marnet-<res>.cjs asset that both the CJS and ESM builds load at
runtime, so importing a variant doesn't duplicate the network across builds.
The 10 km and 5 km networks are large enough that bundling them would dominate
the install, so they are not shipped in the package. Generate them from the
Eurostat source with scripts/build-marnet.cjs (the script header documents the
GDAL conversion), host the resulting JSON, and load it with
loadNetwork
— or pass any FeatureCollection<LineString> to the network option directly.
Output shape
length runs between the two snapped network vertices. It does not include
the legs from your inputs to the network (originSnapKm, destinationSnapKm,
always in km), even with appendOriginDestination: true, which only adds the
raw points to the line. For a door-to-door figure, add them yourself:
On hops shorter than the network resolution (~100 km by default, ~20 km with
searoute-ts/marnet-20km), the snapped vertices can sit closer together than
the inputs, so length can come out below greatCircleLength
(detourRatio < 1). If both inputs snap to the same vertex, NoRouteError is
thrown.
Full options
Inputs can be [lon, lat] arrays, GeoJSON Feature<Point>, bare Point objects,
or a UN/LOCODE string (e.g. 'CNSHA') once searoute-ts/ports is imported.
Antimeridian (dateline) handling
Routes that cross the ±180° meridian (e.g. Yokohama → LA) come back wrapped to
[-180, 180] by default, which many map renderers draw as a straight streak
across the whole map. Pass antimeridian to get map-ready geometry:
'unwrap' shifts longitudes by multiples of 360° so the line never jumps the
dateline (ideal for MapLibre/Leaflet/Deck.gl). 'split' cuts the route into a
MultiLineString at ±180°, keeping every coordinate in range. Both apply to
seaRoute and seaRouteMulti; properties.length is unchanged either way.
Forcing routes through a passage (via)
restrictions blocks a passage; via requires one — the inverse. Use
it to compare explicit routings, e.g. "via Suez" against "via Cape of Good Hope",
or to force a Pacific + Panama routing between Asia and Europe:
via accepts the same passage names as restrictions and visits multiple
passages in the order given. It routes origin → passage → destination through
each passage's location using the multi-leg machinery, so it composes with the
other options. A passage named in via is never blocked out from under the
requirement (via: ['northeast'] reaches the Northeast Passage without also
needing allowArctic). Naming the same passage in both via and restrictions
is a contradiction and throws NoRouteError.
Emissions & ECA/SECA reporting
Opt in with emissions: true for two rough estimates on properties:
ecaKm— how much of the route lies inside ECA/SECA emission-control areas (Baltic, North Sea, Mediterranean, North American and US Caribbean), which drives fuel-type/cost. The zones ship behind thesearoute-ts/ecasubpath export (to keep the core lean); importing it registers them. They are bounding-box approximations of the IMO MARPOL Annex VI areas — good for estimates, not compliance. Swap in higher-fidelity polygons withregisterEcaZones.co2eTonnes— a deliberately simpledistance × vessel-class factorestimate, not a certified figure. Factors are derived transparently from a representative fuel burn and the IMO HFO CO₂ conversion (seeVESSEL_CLASSES); override withco2eFactorKgPerKm. GLEC recommends inflating shortest-path distance by ~15 % for real-world deviations — passglecInflation: 0.15.
Restrictable passages
The first twelve are natively labelled in the Eurostat marnet (exact match
on the feature's pass attribute). The remaining four are detected via
bounding boxes.
The Northwest and Northeast Passages are mathematically the shortest path for
many Asia ↔ Europe routes but are ice-blocked most of the year, so they are
blocked by default. Opt in with allowArctic: true.
Validated against industry distances
12 real-world lanes within ±10% of published Searoutes / Sea-Distances figures.
All checks pass in the test suite.
Errors
SnapFailedError— input cannot be projected onto the network withinmaxSnapDistanceKm. Carries.side: 'origin' | 'destination'and.distanceKm: number.NoRouteError— no path exists between the snapped origin and destination (e.g. all viable canals blocked).
API reference
Port codes (UN/LOCODE)
Origins and destinations may be given as UN/LOCODE strings (e.g. 'CNSHA')
instead of coordinates. The port dataset ships behind the searoute-ts/ports
subpath export, so consumers only pay for it if they use it — importing the
subpath (for any of its exports, or purely for its side effect) registers a
resolver into the core so seaRoute('CNSHA', 'NLRTM') works.
- ~1 600 seaports, keyed by UN/LOCODE (primary codes and aliases).
- Source: marchah/sea-ports (MIT),
itself derived from UN/LOCODE. Regenerate with
scripts/build-ports.cjs. - Coordinates are approximate (port-city granularity) — the routing engine snaps them onto the network anyway, so this is fine for distance/visualisation.
- Unknown or malformed codes throw
UnknownPortError.
Use from an AI agent (MCP)
A companion Model Context Protocol server,
@searoute-ts/mcp (source),
lets AI agents (Claude Desktop, the claude CLI, etc.) compute real sea routes
instead of guessing — asking "how far is Shanghai to Rotterdam by sea, avoiding
Suez?" calls the library directly. It exposes two tools, sea_route and
sea_route_alternatives, and accepts port codes ('CNSHA') or coordinates.
Or add it to any MCP client config:
See the server's README
for the full tool reference. For the rail leg, add
@railroute-ts/mcp alongside it
(claude mcp add railroute -- npx -y @railroute-ts/mcp).
Multimodal: add the rail leg (railroute-ts)
Sea distance is rarely the whole shipment. The sibling library
railroute-ts routes over the
OpenStreetMap rail network (Europe bundled, same API shape, same GeoJSON
output), so a port-to-inland quote or a GLEC/CountEmissions-style report is one
extra call:
npm install railroute-ts — docs & interactive demo.
Both libraries also ship MCP servers, so an AI agent can chain sea_route →
rail_route for door-to-door distance (see below).
How it works
A two-page deep-dive (graph data, snapping, Dijkstra, restrictions, antimeridian fix, draft logic, alternatives) is in DOCS.md.
FAQ
Is this for navigation? No. The routes are network paths suitable for visualisation and rough distance/duration estimates, not for piloting ships.
Does it support weather routing? No. For weather-aware routing see VISIR-2.
Why are my Asia→Europe routes going through Bering Strait? They aren't,
by default — the Northwest and Northeast Passages are blocked. Pass
allowArctic: true to enable them.
Can I use my own network? Yes — seaRoute(origin, destination, { network }).
Useful for inland waterways or AIS-derived custom graphs. For higher-resolution
Eurostat data (5/10/20/50 km), see
Higher-resolution networks — 20 km and
50 km ship as subpath exports.
Does it handle the Red Sea / Suez crisis? Yes — pass
restrictions: ['suez', 'babelmandeb'] to force Cape of Good Hope routing.
Cannot find module '…/searoute-ts/dist/lib/utils' on 1.x? 1.x (up to
1.2.1) shipped an ESM build with extensionless imports under "main", so plain
Node can't load it without patching. Fixed in 2.0: install searoute-ts@^2,
because a ^1 range never picks it up. When upgrading, use the named
import { seaRoute }. Failures now throw NoRouteError / SnapFailedError
instead of returning null, and nautical-mile lengths are ~24 % smaller,
because 1.x over-counted them. See the 2.0.0 migration notes in the
CHANGELOG.
Is the great-circle distance correct across the antimeridian? Yes — the marnet has been normalised so the Pacific is a connected graph, and all distances use haversine internally.
What's the bundle size? What you import at runtime is small: the core plus
the bundled 100 km marnet (~1.1 MB JSON, shipped once as a shared
dist/data/marnet.cjs asset both builds load, rather than inlined into each).
Tree-shakeable, so the optional searoute-ts/marnet-20km / marnet-50km
networks only load if you import them. They do add to the npm tarball, though —
including them the package is ~1.1 MB packed / ~7 MB unpacked (each variant is a
single shared asset, not duplicated per build). If you need the finer networks
without the install cost, generate and host them and use loadNetwork instead.
Credits
- Maritime network — Eurostat searoute v3.5 (EUPL-1.2). Oak Ridge National Labs Global Shipping Lane Network enriched with European AIS data.
- Dijkstra —
geojson-path-finder@2by Per Liedman. - Inspired by
searoute-py(Apache-2.0). - Original JS port — @johnx25bd.
- Geospatial primitives — Turf.js.
License
MIT © Mayur Rawte
来源:README.md,提交 f837ac6
工具
0版本历史
1- v0.1.1最新Sep 16, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分