1 | # Leaflet.Geodesic
|
2 | [![Build Status](https://travis-ci.org/henrythasler/Leaflet.Geodesic.svg?branch=master)](https://travis-ci.org/henrythasler/Leaflet.Geodesic) [![npm](https://img.shields.io/npm/v/leaflet.geodesic)](https://www.npmjs.com/package/leaflet.geodesic) [![Coverage Status](https://coveralls.io/repos/github/henrythasler/Leaflet.Geodesic/badge.svg?branch=master)](https://coveralls.io/github/henrythasler/Leaflet.Geodesic?branch=master) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=henrythasler_Leaflet.Geodesic&metric=alert_status)](https://sonarcloud.io/dashboard?id=henrythasler_Leaflet.Geodesic)
|
3 |
|
4 | Add-on for [Leaflet](http://leafletjs.com/) to draw [geodesic](http://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid) lines and circles. A geodesic line is the shortest path between two given positions on the earth surface. It's based on [Vincenty's formulae](https://en.wikipedia.org/wiki/Vincenty%27s_formulae) implemented by [Chris Veness](https://github.com/chrisveness/geodesy) for highest precision.
|
5 |
|
6 | [![demo](docs/img/demo.png)](https://blog.cyclemap.link/Leaflet.Geodesic/basic-interactive.html)
|
7 |
|
8 | [Live Demos and Tutorials](https://blog.cyclemap.link/Leaflet.Geodesic/)
|
9 |
|
10 | [Observable-Notebook](https://observablehq.com/@henrythasler/leaflet-geodesic)
|
11 |
|
12 | [API-Documentation](https://blog.cyclemap.link/Leaflet.Geodesic/api)
|
13 |
|
14 | ## Add the plugin to your project
|
15 |
|
16 | Leaflet.Geodesic is available via CDN. Add the following snippet to your html-file after you have [included leaflet.js](https://leafletjs.com/examples/quick-start/).
|
17 |
|
18 | ```html
|
19 | <!-- Make sure you put this AFTER leaflet.js -->
|
20 | <script src="https://cdn.jsdelivr.net/npm/leaflet.geodesic"></script>
|
21 | ```
|
22 |
|
23 | Leaflet.Geodesic is available from [unpkg](https://unpkg.com/browse/leaflet.geodesic/), [jsDelivr](https://www.jsdelivr.com/package/npm/leaflet.geodesic) and [npmjs](https://www.npmjs.com/package/leaflet.geodesic).
|
24 |
|
25 | Add it in your nodejs-project with `npm i leaflet.geodesic`.
|
26 |
|
27 | If possible, pin the plug-in to a specific version and use [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). Check the [release page](https://github.com/henrythasler/Leaflet.Geodesic/releases) for the latest version, links and checksum. A checksum can by verified with `npm run build`, is stored in `dist/leaflet.geodesic.umd.min.js.sha256` on [jsDelivr](https://www.jsdelivr.com/package/npm/leaflet.geodesic?path=dist) and [unpkg](https://unpkg.com/browse/leaflet.geodesic@2.5.1/dist/leaflet.geodesic.umd.min.js.sha256) and is shown in the [build-log](https://travis-ci.org/henrythasler/Leaflet.Geodesic/builds) for a tagged version.
|
28 |
|
29 | ## Basic usage
|
30 |
|
31 | - `L.Geodesic` draws geodesic lines between all points of a given line- or multiline-string.
|
32 | - `L.GeodesicCircle` draws a circle with a specific radius around a given point.
|
33 |
|
34 | The Objects can be created as follows:
|
35 |
|
36 | ```JavaScript
|
37 | const geodesicLine = new L.Geodesic().addTo(map); // creates a blank geodesic-line-object and adds it to the map
|
38 | const geodesicCircle = new L.GeodesicCircle().addTo(map); // creates a blank geodesic-circle-object and adds it to the map
|
39 | ```
|
40 |
|
41 | Alternative method:
|
42 |
|
43 | ```JavaScript
|
44 | const geodesicLine = L.geodesic().addTo(map); // lower-case, w/o new-keyword
|
45 | const geodesicCircle = L.geodesiccircle().addTo(map); // lower-case, w/o new-keyword
|
46 | ```
|
47 |
|
48 | Make sure you add the geodesic-object to the map. It won't display otherwise.
|
49 |
|
50 | Each constructor is defined as:
|
51 | ```JavaScript
|
52 | Geodesic(latlngs?: L.LatLngExpression[] | L.LatLngExpression[][], options?: GeodesicOptions)
|
53 | GeodesicCircle(center?: L.LatLngExpression, options?: GeodesicOptions)
|
54 | ```
|
55 |
|
56 | Both classes are extended from [L.Polyline](http://leafletjs.com/reference.html#polyline), so all methods, events and options for `L.Polyline` can be used with `L.Geodesic` and `L.GeodesicCircle` here as well.
|
57 |
|
58 | ## Geodesic Lines
|
59 |
|
60 | This draws a line. The geometry (points) to use can be given during creation as:
|
61 |
|
62 | ### Objects (Literals)
|
63 |
|
64 | ```JavaScript
|
65 | const Berlin = {lat: 52.5, lng: 13.35};
|
66 | const LosAngeles = {lat: 33.82, lng: -118.38};
|
67 | const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
|
68 | ```
|
69 |
|
70 | ### LatLng-Class
|
71 |
|
72 | ```JavaScript
|
73 | const Berlin = new L.LatLng(52.5, 13.35);
|
74 | const LosAngeles = new L.LatLng(33.82, -118.38);
|
75 | const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
|
76 | ```
|
77 |
|
78 | ### Tuples
|
79 |
|
80 | ```JavaScript
|
81 | const Berlin = [52.5, 13.35];
|
82 | const LosAngeles = [33.82, -118.38];
|
83 | const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
|
84 | ```
|
85 |
|
86 | ![line](docs/img/line.png)
|
87 |
|
88 | ### Line-strings
|
89 |
|
90 | Multiple consecutive points can be given as an array (linestring):
|
91 |
|
92 | ```JavaScript
|
93 | const places = [
|
94 | new L.LatLng(52.5, 13.35), // Berlin
|
95 | new L.LatLng(33.82, -118.38), // Los Angeles
|
96 | new L.LatLng(-33.44, -70.71), // Santiago
|
97 | new L.LatLng(-33.94, 18.39), // Capetown
|
98 | ];
|
99 | const geodesic = new L.Geodesic(places).addTo(map);
|
100 | ```
|
101 |
|
102 | ![linestring](docs/img/linestring.png)
|
103 |
|
104 | ### Multi-line-strings
|
105 |
|
106 | Multiple independent linestrings can be defined as a 2-dimensional array of points:
|
107 |
|
108 | ```JavaScript
|
109 | const places = [
|
110 | [ // 1st line
|
111 | new L.LatLng(52.5, 13.35), // Berlin
|
112 | new L.LatLng(33.82, -118.38), // Los Angeles
|
113 | ],
|
114 | [ // 2nd line
|
115 | new L.LatLng(-33.44, -70.71), // Santiago
|
116 | new L.LatLng(-33.94, 18.39), // Capetown
|
117 | ]
|
118 | ];
|
119 | const geodesic = new L.Geodesic(places).addTo(map);
|
120 | ```
|
121 |
|
122 | ![multilinestring](docs/img/multilinestring.png)
|
123 |
|
124 | ### GeoJSON-Support
|
125 |
|
126 | GeoJSON-data can be used to create geodesic lines with the `fromGeoJson()` method:
|
127 |
|
128 | ```JavaScript
|
129 | const geojson = {
|
130 | "type": "LineString",
|
131 | "coordinates": [
|
132 | [13.35, 52.5], [-122.33, 47.56], [18.39, -33.94], [116.39, 39.92], [13.35, 52.5]
|
133 | ]
|
134 | };
|
135 | const geodesic = new L.Geodesic().addTo(map);
|
136 | geodesic.fromGeoJson(geojson);
|
137 | ```
|
138 |
|
139 | ![geojson](docs/img/geojson.png)
|
140 |
|
141 | ### Updating the geometry
|
142 |
|
143 | #### Set new geometry
|
144 |
|
145 | The Geodesic-Class provides a `setLatLngs()`-Method, that can be used to update the geometry of an existing `L.Geodesic`-object:
|
146 |
|
147 | ```Javascript
|
148 | const geodesic = new L.Geodesic().addTo(map); // add empty object to the map
|
149 |
|
150 | const Berlin = new L.LatLng(52.5, 13.35);
|
151 | const LosAngeles = new L.LatLng(33.82, -118.38);
|
152 |
|
153 | geodesic.setLatLngs([Berlin, LosAngeles]) // update in-place
|
154 | ```
|
155 |
|
156 | The `setLatLngs()`-Method accepts the same types (Literal, Tuple, LatLang-Class, Linstring, Multilinestring) as the L.Geodesic-constructor itself. Please refer to the section about geodesic circles below, on how to update a circle geometry.
|
157 |
|
158 | #### Delete geometry
|
159 |
|
160 | Delete the existing geometry by setting an empty array `geodesic.setLatLngs([])`.
|
161 |
|
162 | #### adding points
|
163 |
|
164 | Points can be added to existing geodesic lines with `addLatLng()`:
|
165 |
|
166 | ```Javascript
|
167 | const Berlin = new L.LatLng(52.5, 13.35);
|
168 | const LosAngeles = new L.LatLng(33.82, -118.38);
|
169 | const Beijing = new L.LatLng(39.92, 116.39);
|
170 |
|
171 | const geodesic = new L.Geodesic([Berlin, LosAngeles]).addTo(map);
|
172 | geodesic.addLatLng(Beijing); // results in [[Berlin, LosAngeles, Beijing]
|
173 | ```
|
174 |
|
175 | The new point will always be added to the last linestring of a multiline. You can define a specific linestring to add to by reading the `points` property before and hand over a specific linestring as second parameter:
|
176 |
|
177 | ```Javascript
|
178 | const Berlin = new L.LatLng(52.5, 13.35);
|
179 | const LosAngeles = new L.LatLng(33.82, -118.38);
|
180 | const Beijing = new L.LatLng(39.92, 116.39 );
|
181 | const Capetown = new L.LatLng(-33.94, 18.39 );
|
182 | const Santiago = new L.LatLng(-33.44, -70.71);
|
183 |
|
184 | const geodesic = new L.Geodesic([[Berlin, LosAngeles], [Santiago, Capetown]]).addTo(map);
|
185 | geodesic.addLatLng(Beijing, geodesic.points[0]); // results in [[Berlin, LosAngeles, Beijing], [Santiago, Capetown]]
|
186 | ```
|
187 |
|
188 | ### Line Options
|
189 | All options defined for [Polyline](http://leafletjs.com/reference.html#polyline) and [Path](https://leafletjs.com/reference.html#path) for can be used Leaflet.Geodesic.
|
190 |
|
191 | The most important options are:
|
192 |
|
193 | Option | Type | Default | Description
|
194 | ---|---|---|---
|
195 | `color` | `String` | "#3388ff" | Stroke color
|
196 | `weight` | `Number` | 3 | Stroke width in pixels
|
197 | `opacity` | `Number` | 1.0 | Stroke opacity (0=transparent, 1=opaque)
|
198 | `steps` | `Number` | 3 | Level of detail (vertices = 1+2**(steps+1)) for the geodesic line. More steps result in a smoother line. Range: 0..8
|
199 | `wrap` | `Boolean` | true | Wrap geodesic line at antimeridian. Set to `false`, to draw a line over the antimeridian. See [no-wrap demo](https://blog.cyclemap.link/Leaflet.Geodesic/nowrap-interactive.html) for example.
|
200 |
|
201 | Example:
|
202 |
|
203 | ```Javascript
|
204 | const Berlin = new L.LatLng(52.5, 13.35);
|
205 | const LosAngeles = new L.LatLng(33.82, -118.38);
|
206 | const options = {
|
207 | weight: 20,
|
208 | opacity: 0.5,
|
209 | color: 'red',
|
210 | };
|
211 | const geodesic = new L.Geodesic([Berlin, LosAngeles], options).addTo(map);
|
212 | ```
|
213 |
|
214 | ![lineoptions](docs/img/lineoptions.png)
|
215 |
|
216 | ## Geodesic Circles
|
217 |
|
218 | Circles can be added with another class called `L.GeodesicCircle` as follows:
|
219 |
|
220 | ```Javascript
|
221 | const Seattle = new L.LatLng(47.56, -122.33);
|
222 | const geodesiccircle = new L.GeodesicCircle(Seattle, {
|
223 | radius: 3000*1000, // 3000km in meters
|
224 | }).addTo(map);
|
225 | ```
|
226 |
|
227 | ![circle](docs/img/circle.png)
|
228 |
|
229 | The geometry of a circle can be updated with the following methods:
|
230 |
|
231 | - `setLatLng(latlng: L.LatLngExpression)` - set a new center
|
232 | - `setRadius(radius: number)` - update the radius
|
233 |
|
234 | Handling of **filled** circles crossing the antimeridian (wrapping) is not yet supported. Set `fill: false` in these cases to avoid display artefacts.
|
235 |
|
236 | ### Circle Options
|
237 |
|
238 | Option | Type | Default | Description
|
239 | ---|---|---|---
|
240 | `radius` | `Number` | 1000*1000 | Radius in **meters**
|
241 | `steps` | `Number` | 24 | Number of segments that are used to approximate the circle.
|
242 | `fill` | `boolean` | true | Draws a filled circle.
|
243 | `color` | `String` | "#3388ff" | Stroke color
|
244 | `weight` | `Number` | 3 | Stroke width in pixels
|
245 | `opacity` | `Number` | 1.0 | Stroke opacity (0=transparent, 1=opaque)
|
246 |
|
247 | Please refer to the options for [Polyline](http://leafletjs.com/reference.html#polyline) and [Path](https://leafletjs.com/reference.html#path) for additional settings.
|
248 |
|
249 | ## Statistics
|
250 |
|
251 | The `L.Geodesic` and `L.GeodesicCircle`-class provide a `statistics`-Object with the following properties:
|
252 |
|
253 | Property | Type | Description
|
254 | ---|---|---
|
255 | `totalDistance` | `Number` | The total distance of all geodesic lines in meters. (Circumfence for `L.GeodesicCircle`)
|
256 | `distanceArray` | `Number[]` | The distance for each separate linestring in meters
|
257 | `points` | `Number` | Number of points that were given on creation or with `setLatLngs()`
|
258 | `vertices` | `Number` | Number of vertices of all geodesic lines that were calculated
|
259 |
|
260 | ## Distance Calculation
|
261 |
|
262 | The `L.Geodesic` provides a `distance`-function to calculate the precise distance between two points:
|
263 |
|
264 | ```Javascript
|
265 | const Berlin = new L.LatLng(52.5, 13.35);
|
266 | const Beijing = new L.LatLng(39.92, 116.39);
|
267 |
|
268 | const line = new L.Geodesic();
|
269 | const distance = line.distance(Berlin, Beijing);
|
270 | console.log(`${Math.floor(distance/1000)} km`) // prints: 7379 km
|
271 | ```
|
272 |
|
273 | The `L.GeodesicCircle`-class provides a `distanceTo`-function to calculate the distance between the current center and any given point:
|
274 |
|
275 | ```Javascript
|
276 | const Berlin = new L.LatLng(52.5, 13.35);
|
277 | const Beijing = new L.LatLng(39.92, 116.39);
|
278 |
|
279 | const circle = new L.GeodesicCircle(Berlin);
|
280 | const distance = circle.distanceTo(Beijing);
|
281 | console.log(`${Math.floor(distance/1000)} km`) // prints: 7379 km
|
282 | ```
|
283 |
|
284 | ## Scientific background
|
285 |
|
286 | All calculations are based on the [WGS84-Ellipsoid](https://en.wikipedia.org/wiki/World_Geodetic_System#WGS84) (EPSG:4326) using [Vincenty's formulae](https://en.wikipedia.org/wiki/Vincenty%27s_formulae). This method leads to very precise calculations but may fail for some corner-cases (e.g. [Antipodes](https://en.wikipedia.org/wiki/Antipodes)). I use some workarounds to mitigate these convergence errors. This may lead to reduced precision (a.k.a. slightly wrong results) in these cases. This is good enough for a web mapping application but you shouldn't plan a space mission based on this data. OMG, this section has just become a disclaimer...
|