{"id":15037446,"url":"https://github.com/chrisveness/geodesy","last_synced_at":"2026-10-19T14:36:52.145Z","repository":{"id":17480219,"uuid":"20259456","full_name":"chrisveness/geodesy","owner":"chrisveness","description":"Libraries of geodesy functions implemented in JavaScript","archived":false,"fork":false,"pushed_at":"2024-07-10T16:54:06.000Z","size":614,"stargazers_count":1227,"open_issues_count":19,"forks_count":207,"subscribers_count":71,"default_branch":"master","last_synced_at":"2026-10-09T22:22:35.828Z","etag":null,"topics":["bearing","datum","destination","distance","ellipsoid","geodesy","haversine","javascript","latitude","longitude","mgrs","n-vector","rhumb","trf","utm","vincenty","wgs84"],"latest_commit_sha":null,"homepage":"http://www.movable-type.co.uk/scripts/geodesy-library.html","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chrisveness.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-05-28T14:00:20.000Z","updated_at":"2026-09-16T04:42:29.000Z","dependencies_parsed_at":"2024-06-18T12:22:57.588Z","dependency_job_id":"445b0129-e93f-458e-9a86-4ca2b5329106","html_url":"https://github.com/chrisveness/geodesy","commit_stats":{"total_commits":275,"total_committers":12,"mean_commits":"22.916666666666668","dds":"0.050909090909090904","last_synced_commit":"761587cd748bd9f7c9825195eba4a9fc5891b859"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/chrisveness/geodesy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisveness%2Fgeodesy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisveness%2Fgeodesy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisveness%2Fgeodesy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisveness%2Fgeodesy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisveness","download_url":"https://codeload.github.com/chrisveness/geodesy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisveness%2Fgeodesy/sbom","scorecard":{"id":281343,"data":{"date":"2025-08-11","repo":{"name":"github.com/chrisveness/geodesy","commit":"3e9fdd35b71dd86cee12383eccdf000ad6741e9d"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 1/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T15:56:08.765Z","repository_id":17480219,"created_at":"2025-08-17T15:56:08.765Z","updated_at":"2025-08-17T15:56:08.765Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":344872684,"owners_count":38358126,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-10-03T21:59:58.778Z","status":"online","status_checked_at":"2026-10-19T02:00:06.414Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bearing","datum","destination","distance","ellipsoid","geodesy","haversine","javascript","latitude","longitude","mgrs","n-vector","rhumb","trf","utm","vincenty","wgs84"],"created_at":"2024-09-24T20:34:38.713Z","updated_at":"2026-10-19T14:36:51.824Z","avatar_url":"https://github.com/chrisveness.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"Geodesy functions\n=================\n\n[![Build Status](https://travis-ci.com/chrisveness/geodesy.svg?branch=master)](https://app.travis-ci.com/github/chrisveness/geodesy)\n[![Coverage Status](https://coveralls.io/repos/github/chrisveness/geodesy/badge.svg)](https://coveralls.io/github/chrisveness/geodesy)\n[![Documentation](https://img.shields.io/badge/docs-www.movable--type.co.uk%2Fscripts%2Fgeodesy--library.html-lightgrey.svg)](https://www.movable-type.co.uk/scripts/geodesy-library.html)\n\nThese libraries started life (a long time ago) as simple ‘latitude/longitude’ code fragments\ncovering distances and bearings, intended to help people who had little experience of geodesy, and\nperhaps limited programming experience.\n\nThe intention was to have clear, simple illustrative code samples which could be adapted and re-used\nin other projects (whether those be coded in JavaScript, Java, C++, Excel VBA, or anything else...).\nWith its untyped C-style syntax, JavaScript reads remarkably close to pseudo-code, exposing the\nalgorithms with a minimum of syntactic distractions\n\nWhile still valid for that purpose, they have grown since then into considerable libraries, based\naround:\n- simpler **trig**-based functions (distance, bearing, etc) based on a **spherical earth** model\n- more sophisticated **trig**-based functions (distance, bearing, etc) based on a\n  more accurate **ellipsoidal earth** model\n- **vector**-based functions mostly based on a **spherical** earth model, with some **ellipsoidal**\n  functions\n\nComplementing these are various mapping-related functions covering:\n- UTM coordinates \u0026 MGRS grid references\n- UK Ordnance Survey (OSGB) national grid references\n\nAnd also functions for historical datum conversions (such as between NAD83, OSGB36, Irl1975, \netc) and modern reference frame conversions (such as ITRF2014, ETRF2000, GDA94, etc), \nand conversions between geodetic (latitude/longitude) coordinates and geocentric cartesian (x/y/z) \ncoordinates.\n\nThere are also supporting libraries:\n- 3d vector manipulation functions (supporting cartesian (x/y/z) coordinates and n-vector geodesy)\n- functions for conversion between decimal degrees and (sexagesimal) degrees/minutes/seconds\n\nThe spherical-earth model provides simple formulae covering most ‘everyday’ accuracy requirements;\nthe ellipsoidal-earth model provides more accurate formulae at the expense of complexity. The\nvector-based functions provide an alternative approach to the trig-based functions, with some\noverlapping functionality; which one to use may depend on availability of related functions or on\nother considerations.\n\nThese functions are as language-agnostic as possible, avoiding excessive use of\nJavaScript-specific language features which would not be recognised by users of other languages\n(and which might be difficult to translate to other languages). I use Greek letters in variables\nrepresenting maths symbols conventionally presented as Greek letters: I value the great benefit in\nlegibility over the minor inconvenience in typing.\n\nThis version 2 of the library uses JavaScript ES classes and modules to organise the \ninterdependencies; this makes the code both more immediately readable than previously, and also more \naccessible to non-JavaScript readers (always  bearing in mind JavaScript uses prototype-based \nclasses rather than classical inheritance-based classes). For older browsers (or Node.js \u003c8.0.0), \n[v1.1.3](https://github.com/chrisveness/geodesy/tree/v1.1.3) is ES5-based. Note that there are \n[breaking changes](https://www.movable-type.co.uk/scripts/geodesy-library-migrating-from-v1.html) \nin moving from version 1 to version 2. \n\nWhile some aspects of the library are quite complex to understand and use, basic usage is simple –\nfor instance:\n\n- to find the distance between two points using a simple spherical earth model:\n\n```javascript\nimport LatLon from 'geodesy/latlon-spherical.js';\nconst p1 = new LatLon(52.205, 0.119);\nconst p2 = new LatLon(48.857, 2.351);\nconst d = p1.distanceTo(p2); // 404.3×10³ m\n```\n\n- or to find the destination point for a given distance and initial bearing on an ellipsoidal model\n  earth:\n\n```javascript\nimport LatLon from 'geodesy/latlon-ellipsoidal-vincenty.js';\nconst p1 = new LatLon(-37.95103, 144.42487);\nconst dist = 54972.271;\nconst brng = 306.86816;\nconst p2 = p1.destinationPoint(dist, brng); // 37.6528°S, 143.9265°E\n```\n\nFull documentation is available at [www.movable-type.co.uk/scripts/geodesy-library.html](https://www.movable-type.co.uk/scripts/geodesy-library.html), \nand tests in the [browser](https://www.movable-type.co.uk/scripts/test/geodesy-test.html) as well as\n[Travis CI](https://travis-ci.org/chrisveness/geodesy).\n\nUsage\n-----\n\nWhile originally intended as illustrative code fragments, these functions can be used ‘as-is’;\neither client-side in-browser, or with Node.js.\n\n### Usage in browser\n\nThe library can be used in the browser by taking a local copy, or loading it from\n    [jsDelivr](https://www.jsdelivr.com/package/npm/geodesy): for example,\n\n```html\n\u003c!doctype html\u003e\u003ctitle\u003egeodesy example\u003c/title\u003e\u003cmeta charset=\"utf-8\"\u003e\n\u003cscript type=\"module\"\u003e\n    import LatLon from 'https://cdn.jsdelivr.net/npm/geodesy@2.4.0/latlon-spherical.min.js';\n\n    const p1 = new LatLon(50.06632, -5.71475);\n    const p2 = new LatLon(58.64402, -3.07009);\n\n    const d = p1.distanceTo(p2);\n    console.assert(d.toFixed(3) == '968874.704');\n\n    const mid = p1.midpointTo(p2);\n    console.assert(mid.toString('dms') == '54° 21′ 44″ N, 004° 31′ 51″ W');\n\u003c/script\u003e\n```\n\n### Usage in Node.js\n\nThe library can be loaded from [npm](https://www.npmjs.com/package/geodesy) to be used in a Node.js app \n(in Node.js v13.2.0+, or Node.js v12.0.0+ using --experimental-modules, or v8.0.0–v12.15.0\u003csup title=\"v12.16.0+ is not compatible with esm@3.2.25\"\u003e*\u003c/sup\u003e) using the [esm](https://www.npmjs.com/package/esm) package:\n\n```shell\n$ npm install geodesy\n$ node\n\u003e const { default: LatLon } = await import('geodesy/latlon-spherical.js');\n\u003e const p1 = new LatLon(50.06632, -5.71475);\n\u003e const p2 = new LatLon(58.64402, -3.07009);\n\u003e const d = p1.distanceTo(p2);\n\u003e console.assert(d.toFixed(3) == '968874.704');\n\u003e const mid = p1.midpointTo(p2);\n\u003e console.assert(mid.toString('dms') == '54° 21′ 44″ N, 004° 31′ 51″ W');\n```\n\nTo some extent, mixins can be used to add methods of a class to a different class, e.g.:\n\n```javascript\nimport LatLon  from 'geodesy/latlon-nvector-ellipsoidal.js';\nimport LatLonV from 'geodesy/latlon-ellipsoidal-vincenty.js';\n\nfor (const method of Object.getOwnPropertyNames(LatLonV.prototype)) {\n    LatLon.prototype[method] = LatLonV.prototype[method];\n}\n\nconst d = new LatLon(51, 0).distanceTo(new LatLon(52, 1)); // vincenty\nconst δ = new LatLon(51, 0).deltaTo(new LatLon(52, 1));    // n-vector\n```\n\nMore care is of course required if there are conflicting constructors or method names.\n\nFor TypeScript users, type definitions are available from DefinitelyTyped: [www.npmjs.com/package/@types/geodesy](https://www.npmjs.com/package/@types/geodesy).\n\n### Other examples\n\nSome examples of calculations possible with the libraries:\n\ne.g. for geodesic distance on an ellipsoidal model earth using Vincenty’s algorithm:\n\n```javascript\nimport LatLon from 'geodesy/latlon-ellipsoidal-vincenty.js';\n\nconst p1 = new LatLon(50.06632, -5.71475);\nconst p2 = new LatLon(58.64402, -3.07009);\n\nconst d = p1.distanceTo(p2);\nconsole.assert(d.toFixed(3) == '969954.166');\n```\n\ne.g. for UTM conversions:\n\n```javascript\nimport Utm from 'geodesy/utm.js';\n\nconst utm = Utm.parse('48 N 377298.745 1483034.794');\nconst latlon = utm.toLatLon();\n\nconsole.assert(latlon.toString('dms', 2) == '13° 24′ 45.00″ N, 103° 52′ 00.00″ E');\nconsole.assert(latlon.toUtm().toString() == '48 N 377298.745 1483034.794';\n```\n\ne.g. for MGRS/NATO map references:\n\n```javascript\nimport Mgrs, { LatLon } from 'geodesy/mgrs.js';\n\nconst mgrs = Mgrs.parse('31U DQ 48251 11932');\nconst latlon = mgrs.toUtm().toLatLon();\nconsole.assert(latlon.toString('dms', 2) == '48° 51′ 29.50″ N, 002° 17′ 40.16″ E');\n\nconst p = LatLon.parse('51°28′40.37″N, 000°00′05.29″W');\nconst ref = p.toUtm().toMgrs();\nconsole.assert(ref.toString() == '30U YC 08215 07233');\n```\n\ne.g. for OS grid references:\n\n```javascript\nimport OsGridRef, { LatLon } from 'geodesy/osgridref.js';\n\nconst gridref = new OsGridRef(651409.903, 313177.270);\n\nconst pWgs84 = gridref.toLatLon();\nconsole.assert(pWgs84.toString('dms', 4) == '52° 39′ 28.7230″ N, 001° 42′ 57.7870″ E');\n\nconst pOsgb = gridref.toLatLon(LatLon.datums.OSGB36);\nconsole.assert(pOsgb.toString('dms', 4) == '52° 39′ 27.2531″ N, 001° 43′ 04.5177″ E');\n```\n\ne.g. for testing if a point is enclosed within a polygon:\n\n```javascript\nimport LatLon from 'geodesy/latlon-nvector-spherical.js';\n\nconst polygon = [ new LatLon(48,2), new LatLon(49,2), new LatLon(49,3), new LatLon(48,3) ];\n\nconst enclosed = new LatLon(48.9,2.4).isEnclosedBy(polygon);\nconsole.assert(enclosed == true);\n```\n\ne.g. greater parsing \u0026 presentation control:\n\n```javascript\nimport LatLon from 'geodesy/latlon-spherical.js';\nDms.separator = ' '; // full-space separator between degrees-minutes-seconds\n\nconst p1 = LatLon.parse({ lat: '50:03:59N', lng: '005:42:53W' });\nconst p2 = LatLon.parse('58°38′38″N, 003°04′12″W');\n\nconst mid = p1.midpointTo(p2);\nconsole.assert(mid.toString('dms') == '54° 21′ 44″ N, 004° 31′ 50″ W');\n```\n\ne.g. datum conversions:\n\n```javascript\nimport LatLon from 'geodesy/latlon-ellipsoidal-datum.js';\n\nconst pWgs84 = new LatLon(53.3444, -6.2577);\n\nconst pIrl1975 = pWgs84.convertDatum(LatLon.datums.Irl1975);\nconsole.assert(pIrl1975.toString() == '53.3442° N, 006.2567° W');\n```\n\n(The format of the import statements will vary according to deployment).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisveness%2Fgeodesy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisveness%2Fgeodesy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisveness%2Fgeodesy/lists"}