leaflet-geodesic-rails 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +30 -0
- data/lib/leaflet/geodesic/rails.rb +9 -0
- data/lib/leaflet/geodesic/rails/version.rb +7 -0
- data/vendor/assets/javascripts/leaflet.geodesic.js +409 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a10437447f4ecbedb10278f52a2c96e175c67311
|
4
|
+
data.tar.gz: 8eb2076cfead12eece7edba2bce9637167a6ffeb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3633df28e6b19f1df6e3d6f31c52b96519723099b2ce662bb8381a75c891d003eaa4670cc45bb7f9a3900dc3c8a15d7c5b5cd38d5667d3733da00fe996597014
|
7
|
+
data.tar.gz: 4279f33c5ff7dcb5fec1595ce27ad73013a32da8bd9f11fff43d971b34ff224b93a797c41bd9457367b5daf917f0c6337a06abd1438b655b48949410984551a0
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Volker Wiegand
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Leaflet.Geodesic for Rails
|
2
|
+
|
3
|
+
Engine wrapper for the Leaflet Geodesic library by @henrythasler.
|
4
|
+
|
5
|
+
https://github.com/henrythasler/Leaflet.Geodesic.git
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'leaflet-geodesic-rails'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install leaflet-geodesic-rails
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Provides the following assets:
|
24
|
+
|
25
|
+
leaflet.geodesic.js
|
26
|
+
|
27
|
+
## License
|
28
|
+
MIT License, full text of license see [here][License]
|
29
|
+
|
30
|
+
[License]: https://github.com/henrythasler/Leaflet.Geodesic/blob/master/LICENSE "LICENSE"
|
@@ -0,0 +1,409 @@
|
|
1
|
+
// This file is part of Leaflet.Geodesic.
|
2
|
+
// Copyright (C) 2014 Henry Thasler
|
3
|
+
// based on code by Chris Veness Copyright (C) 2014 https://github.com/chrisveness/geodesy
|
4
|
+
//
|
5
|
+
// Leaflet.Geodesic is free software: you can redistribute it and/or modify
|
6
|
+
// it under the terms of the GNU General Public License as published by
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
8
|
+
// (at your option) any later version.
|
9
|
+
//
|
10
|
+
// Leaflet.Geodesic is distributed in the hope that it will be useful,
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
// GNU General Public License for more details.
|
14
|
+
//
|
15
|
+
// You should have received a copy of the GNU General Public License
|
16
|
+
// along with Leaflet.Geodesic. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
/** Extend Number object with method to convert numeric degrees to radians */
|
21
|
+
if (typeof Number.prototype.toRadians == 'undefined') {
|
22
|
+
Number.prototype.toRadians = function() { return this * Math.PI / 180; }
|
23
|
+
}
|
24
|
+
|
25
|
+
/** Extend Number object with method to convert radians to numeric (signed) degrees */
|
26
|
+
if (typeof Number.prototype.toDegrees == 'undefined') {
|
27
|
+
Number.prototype.toDegrees = function() { return this * 180 / Math.PI; }
|
28
|
+
}
|
29
|
+
|
30
|
+
var INTERSECT_LNG = 179.999; // Lng used for intersection and wrap around on map edges
|
31
|
+
|
32
|
+
L.Geodesic = L.MultiPolyline.extend({
|
33
|
+
options: {
|
34
|
+
color:'blue',
|
35
|
+
steps: 10,
|
36
|
+
dash: 1
|
37
|
+
},
|
38
|
+
|
39
|
+
initialize: function (latlngs, options) {
|
40
|
+
this.options = this._merge_options(this.options, options);
|
41
|
+
this.datum = {};
|
42
|
+
this.datum.ellipsoid = { a: 6378137, b: 6356752.3142, f: 1/298.257223563 }; // WGS-84
|
43
|
+
L.MultiPolyline.prototype.initialize.call(this, latlngs, this.options);
|
44
|
+
},
|
45
|
+
|
46
|
+
setLatLngs: function (latlngs) {
|
47
|
+
this._latlngs = (this.options.dash<1)?this._generate_GeodesicDashed(latlngs):this._generate_Geodesic(latlngs);
|
48
|
+
L.MultiPolyline.prototype.setLatLngs.call(this, this._latlngs);
|
49
|
+
},
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Calculates some statistic values of current geodesic multipolyline
|
53
|
+
* @returns (Object} Object with several properties (e.g. overall distance)
|
54
|
+
*/
|
55
|
+
getStats: function () {
|
56
|
+
var obj={ distance: 0,
|
57
|
+
points: 0,
|
58
|
+
polygons: this._latlngs.length
|
59
|
+
}, poly, points;
|
60
|
+
|
61
|
+
for(poly=0; poly<this._latlngs.length;poly++) {
|
62
|
+
obj.points+=this._latlngs[poly].length;
|
63
|
+
for(points=0;points<(this._latlngs[poly].length-1);points++) {
|
64
|
+
obj.distance += this._vincenty_inverse(this._latlngs[poly][points], this._latlngs[poly][points+1]).distance;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
return obj;
|
68
|
+
},
|
69
|
+
|
70
|
+
/**
|
71
|
+
* Creates a great circle. Replaces all current lines.
|
72
|
+
* @param {Object} center - geographic position
|
73
|
+
* @param {number} radius - radius of the circle in meters
|
74
|
+
*/
|
75
|
+
createCircle: function (center, radius) {
|
76
|
+
var _geo = [], _geocnt=0;
|
77
|
+
var prev = {lat:0, lng:0, brg:0};//new L.LatLng(0, 0);
|
78
|
+
var s;
|
79
|
+
|
80
|
+
_geo[_geocnt] = [];
|
81
|
+
for(s=0; s<=this.options.steps; ) {
|
82
|
+
var direct = this._vincenty_direct(center, 360/this.options.steps*s, radius);
|
83
|
+
var gp = new L.LatLng(direct.lat, direct.lng);
|
84
|
+
if(Math.abs(gp.lng-prev.lng) > 180) {
|
85
|
+
var inverse = this._vincenty_inverse(prev, gp);
|
86
|
+
var sec = this._intersection(prev, inverse.initialBearing, {lat: -89, lng:((gp.lng-prev.lng)>0)?-INTERSECT_LNG:INTERSECT_LNG}, 0);
|
87
|
+
if(sec) {
|
88
|
+
_geo[_geocnt].push(new L.LatLng(sec.lat, sec.lng));
|
89
|
+
_geocnt++;
|
90
|
+
_geo[_geocnt] = [];
|
91
|
+
prev = new L.LatLng(sec.lat, -sec.lng);
|
92
|
+
_geo[_geocnt].push(prev);
|
93
|
+
}
|
94
|
+
else {
|
95
|
+
_geocnt++;
|
96
|
+
_geo[_geocnt] = [];
|
97
|
+
_geo[_geocnt].push(gp);
|
98
|
+
prev = gp;
|
99
|
+
s++;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
else {
|
103
|
+
_geo[_geocnt].push(gp);
|
104
|
+
prev = gp;
|
105
|
+
s++;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
this._latlngs = _geo;
|
110
|
+
L.MultiPolyline.prototype.setLatLngs.call(this, this._latlngs);
|
111
|
+
},
|
112
|
+
|
113
|
+
/**
|
114
|
+
* Creates a geodesic MultiPolyline from given coordinates
|
115
|
+
* @param {Object} latlngs - One or more polylines as an array. See Leaflet doc about MultiPolyline
|
116
|
+
* @returns (Object} An array of arrays of geographical points.
|
117
|
+
*/
|
118
|
+
_generate_Geodesic: function (latlngs) {
|
119
|
+
var _geo = [], _geocnt=0, s, poly, points;
|
120
|
+
// _geo = latlngs; // bypass
|
121
|
+
|
122
|
+
for(poly=0; poly<latlngs.length;poly++) {
|
123
|
+
_geo[_geocnt] = [];
|
124
|
+
for(points=0;points<(latlngs[poly].length-1);points++) {
|
125
|
+
var inverse = this._vincenty_inverse(latlngs[poly][points], latlngs[poly][points+1]);
|
126
|
+
var prev = latlngs[poly][points];
|
127
|
+
_geo[_geocnt].push(prev);
|
128
|
+
for(s=1; s<=this.options.steps; ) {
|
129
|
+
var direct = this._vincenty_direct(latlngs[poly][points], inverse.initialBearing, inverse.distance/this.options.steps*s);
|
130
|
+
var gp = new L.LatLng(direct.lat, direct.lng);
|
131
|
+
if(Math.abs(gp.lng-prev.lng) > 180) {
|
132
|
+
var sec = this._intersection(latlngs[poly][points], inverse.initialBearing, {lat: -89, lng:((gp.lng-prev.lng)>0)?-INTERSECT_LNG:INTERSECT_LNG}, 0);
|
133
|
+
if(sec) {
|
134
|
+
_geo[_geocnt].push(new L.LatLng(sec.lat, sec.lng));
|
135
|
+
_geocnt++;
|
136
|
+
_geo[_geocnt] = [];
|
137
|
+
prev = new L.LatLng(sec.lat, -sec.lng);
|
138
|
+
_geo[_geocnt].push(prev);
|
139
|
+
}
|
140
|
+
else {
|
141
|
+
_geocnt++;
|
142
|
+
_geo[_geocnt] = [];
|
143
|
+
_geo[_geocnt].push(gp);
|
144
|
+
prev = gp;
|
145
|
+
s++;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
else {
|
149
|
+
_geo[_geocnt].push(gp);
|
150
|
+
prev = gp;
|
151
|
+
s++;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
_geocnt++;
|
156
|
+
}
|
157
|
+
return _geo;
|
158
|
+
},
|
159
|
+
|
160
|
+
|
161
|
+
/**
|
162
|
+
* Creates a dashed geodesic MultiPolyline from given coordinates - under work
|
163
|
+
* @param {Object} latlngs - One or more polylines as an array. See Leaflet doc about MultiPolyline
|
164
|
+
* @returns (Object} An array of arrays of geographical points.
|
165
|
+
*/
|
166
|
+
_generate_GeodesicDashed: function (latlngs) {
|
167
|
+
var _geo = [], _geocnt=0, s, poly, points;
|
168
|
+
// _geo = latlngs; // bypass
|
169
|
+
|
170
|
+
for(poly=0; poly<latlngs.length;poly++) {
|
171
|
+
_geo[_geocnt] = [];
|
172
|
+
for(points=0;points<(latlngs[poly].length-1);points++) {
|
173
|
+
var inverse = this._vincenty_inverse(latlngs[poly][points], latlngs[poly][points+1]);
|
174
|
+
var prev = latlngs[poly][points];
|
175
|
+
_geo[_geocnt].push(prev);
|
176
|
+
for(s=1; s<=this.options.steps; ) {
|
177
|
+
var direct = this._vincenty_direct(latlngs[poly][points], inverse.initialBearing, inverse.distance/this.options.steps*s-inverse.distance/this.options.steps*(1-this.options.dash));
|
178
|
+
var gp = new L.LatLng(direct.lat, direct.lng);
|
179
|
+
if(Math.abs(gp.lng-prev.lng) > 180) {
|
180
|
+
var sec = this._intersection(latlngs[poly][points], inverse.initialBearing, {lat: -89, lng:((gp.lng-prev.lng)>0)?-INTERSECT_LNG:INTERSECT_LNG}, 0);
|
181
|
+
if(sec) {
|
182
|
+
_geo[_geocnt].push(new L.LatLng(sec.lat, sec.lng));
|
183
|
+
_geocnt++;
|
184
|
+
_geo[_geocnt] = [];
|
185
|
+
prev = new L.LatLng(sec.lat, -sec.lng);
|
186
|
+
_geo[_geocnt].push(prev);
|
187
|
+
}
|
188
|
+
else {
|
189
|
+
_geocnt++;
|
190
|
+
_geo[_geocnt] = [];
|
191
|
+
_geo[_geocnt].push(gp);
|
192
|
+
prev = gp;
|
193
|
+
s++;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
else {
|
197
|
+
_geo[_geocnt].push(gp);
|
198
|
+
_geocnt++;
|
199
|
+
var direct2 = this._vincenty_direct(latlngs[poly][points], inverse.initialBearing, inverse.distance/this.options.steps*s);
|
200
|
+
_geo[_geocnt] = [];
|
201
|
+
_geo[_geocnt].push(new L.LatLng(direct2.lat, direct2.lng));
|
202
|
+
s++;
|
203
|
+
}
|
204
|
+
}
|
205
|
+
}
|
206
|
+
_geocnt++;
|
207
|
+
}
|
208
|
+
return _geo;
|
209
|
+
},
|
210
|
+
|
211
|
+
|
212
|
+
/**
|
213
|
+
* Vincenty direct calculation.
|
214
|
+
* based on the work of Chris Veness (https://github.com/chrisveness/geodesy)
|
215
|
+
*
|
216
|
+
* @private
|
217
|
+
* @param {number} initialBearing - Initial bearing in degrees from north.
|
218
|
+
* @param {number} distance - Distance along bearing in metres.
|
219
|
+
* @returns (Object} Object including point (destination point), finalBearing.
|
220
|
+
*/
|
221
|
+
|
222
|
+
_vincenty_direct : function (p1, initialBearing, distance) {
|
223
|
+
var φ1 = p1.lat.toRadians(), λ1 = p1.lng.toRadians();
|
224
|
+
var α1 = initialBearing.toRadians();
|
225
|
+
var s = distance;
|
226
|
+
|
227
|
+
var a = this.datum.ellipsoid.a, b = this.datum.ellipsoid.b, f = this.datum.ellipsoid.f;
|
228
|
+
|
229
|
+
var sinα1 = Math.sin(α1);
|
230
|
+
var cosα1 = Math.cos(α1);
|
231
|
+
|
232
|
+
var tanU1 = (1-f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1 * cosU1;
|
233
|
+
var σ1 = Math.atan2(tanU1, cosα1);
|
234
|
+
var sinα = cosU1 * sinα1;
|
235
|
+
var cosSqα = 1 - sinα*sinα;
|
236
|
+
var uSq = cosSqα * (a*a - b*b) / (b*b);
|
237
|
+
var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
|
238
|
+
var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
|
239
|
+
|
240
|
+
var σ = s / (b*A), σʹ, iterations = 0;
|
241
|
+
do {
|
242
|
+
var cos2σM = Math.cos(2*σ1 + σ);
|
243
|
+
var sinσ = Math.sin(σ);
|
244
|
+
var cosσ = Math.cos(σ);
|
245
|
+
var Δσ = B*sinσ*(cos2σM+B/4*(cosσ*(-1+2*cos2σM*cos2σM)-
|
246
|
+
B/6*cos2σM*(-3+4*sinσ*sinσ)*(-3+4*cos2σM*cos2σM)));
|
247
|
+
σʹ = σ;
|
248
|
+
σ = s / (b*A) + Δσ;
|
249
|
+
} while (Math.abs(σ-σʹ) > 1e-12 && ++iterations);
|
250
|
+
|
251
|
+
var x = sinU1*sinσ - cosU1*cosσ*cosα1;
|
252
|
+
var φ2 = Math.atan2(sinU1*cosσ + cosU1*sinσ*cosα1, (1-f)*Math.sqrt(sinα*sinα + x*x));
|
253
|
+
var λ = Math.atan2(sinσ*sinα1, cosU1*cosσ - sinU1*sinσ*cosα1);
|
254
|
+
var C = f/16*cosSqα*(4+f*(4-3*cosSqα));
|
255
|
+
var L = λ - (1-C) * f * sinα *
|
256
|
+
(σ + C*sinσ*(cos2σM+C*cosσ*(-1+2*cos2σM*cos2σM)));
|
257
|
+
var λ2 = (λ1+L+3*Math.PI)%(2*Math.PI) - Math.PI; // normalise to -180...+180
|
258
|
+
|
259
|
+
var revAz = Math.atan2(sinα, -x);
|
260
|
+
|
261
|
+
return {lat: φ2.toDegrees(),
|
262
|
+
lng: λ2.toDegrees(),
|
263
|
+
finalBearing: revAz.toDegrees()
|
264
|
+
};
|
265
|
+
},
|
266
|
+
|
267
|
+
/**
|
268
|
+
* Vincenty inverse calculation.
|
269
|
+
* based on the work of Chris Veness (https://github.com/chrisveness/geodesy)
|
270
|
+
*
|
271
|
+
* @private
|
272
|
+
* @param {LatLng} p1 - Latitude/longitude of start point.
|
273
|
+
* @param {LatLng} p2 - Latitude/longitude of destination point.
|
274
|
+
* @returns {Object} Object including distance, initialBearing, finalBearing.
|
275
|
+
* @throws {Error} If formula failed to converge.
|
276
|
+
*/
|
277
|
+
_vincenty_inverse: function (p1, p2) {
|
278
|
+
var φ1 = p1.lat.toRadians(), λ1 = p1.lng.toRadians();
|
279
|
+
var φ2 = p2.lat.toRadians(), λ2 = p2.lng.toRadians();
|
280
|
+
|
281
|
+
var a = this.datum.ellipsoid.a, b = this.datum.ellipsoid.b, f = this.datum.ellipsoid.f;
|
282
|
+
|
283
|
+
var L = λ2 - λ1;
|
284
|
+
var tanU1 = (1-f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1 * cosU1;
|
285
|
+
var tanU2 = (1-f) * Math.tan(φ2), cosU2 = 1 / Math.sqrt((1 + tanU2*tanU2)), sinU2 = tanU2 * cosU2;
|
286
|
+
|
287
|
+
var λ = L, λʹ, iterations = 0;
|
288
|
+
do {
|
289
|
+
var sinλ = Math.sin(λ), cosλ = Math.cos(λ);
|
290
|
+
var sinSqσ = (cosU2*sinλ) * (cosU2*sinλ) + (cosU1*sinU2-sinU1*cosU2*cosλ) * (cosU1*sinU2-sinU1*cosU2*cosλ);
|
291
|
+
var sinσ = Math.sqrt(sinSqσ);
|
292
|
+
if (sinσ==0) return 0; // co-incident points
|
293
|
+
var cosσ = sinU1*sinU2 + cosU1*cosU2*cosλ;
|
294
|
+
var σ = Math.atan2(sinσ, cosσ);
|
295
|
+
var sinα = cosU1 * cosU2 * sinλ / sinσ;
|
296
|
+
var cosSqα = 1 - sinα*sinα;
|
297
|
+
var cos2σM = cosσ - 2*sinU1*sinU2/cosSqα;
|
298
|
+
if (isNaN(cos2σM)) cos2σM = 0; // equatorial line: cosSqα=0 (§6)
|
299
|
+
var C = f/16*cosSqα*(4+f*(4-3*cosSqα));
|
300
|
+
λʹ = λ;
|
301
|
+
λ = L + (1-C) * f * sinα * (σ + C*sinσ*(cos2σM+C*cosσ*(-1+2*cos2σM*cos2σM)));
|
302
|
+
} while (Math.abs(λ-λʹ) > 1e-12 && ++iterations<100);
|
303
|
+
if (iterations>=100) {
|
304
|
+
console.log('Formula failed to converge. Altering target position.')
|
305
|
+
return this._vincenty_inverse(p1, {lat: p2.lat, lng:p2.lng-0.01})
|
306
|
+
// throw new Error('Formula failed to converge');
|
307
|
+
}
|
308
|
+
|
309
|
+
var uSq = cosSqα * (a*a - b*b) / (b*b);
|
310
|
+
var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
|
311
|
+
var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
|
312
|
+
var Δσ = B*sinσ*(cos2σM+B/4*(cosσ*(-1+2*cos2σM*cos2σM)-
|
313
|
+
B/6*cos2σM*(-3+4*sinσ*sinσ)*(-3+4*cos2σM*cos2σM)));
|
314
|
+
|
315
|
+
var s = b*A*(σ-Δσ);
|
316
|
+
|
317
|
+
var fwdAz = Math.atan2(cosU2*sinλ, cosU1*sinU2-sinU1*cosU2*cosλ);
|
318
|
+
var revAz = Math.atan2(cosU1*sinλ, -sinU1*cosU2+cosU1*sinU2*cosλ);
|
319
|
+
|
320
|
+
s = Number(s.toFixed(3)); // round to 1mm precision
|
321
|
+
return { distance: s, initialBearing: fwdAz.toDegrees(), finalBearing: revAz.toDegrees() };
|
322
|
+
},
|
323
|
+
|
324
|
+
|
325
|
+
/**
|
326
|
+
* Returns the point of intersection of two paths defined by point and bearing.
|
327
|
+
* based on the work of Chris Veness (https://github.com/chrisveness/geodesy)
|
328
|
+
*
|
329
|
+
* @param {LatLon} p1 - First point.
|
330
|
+
* @param {number} brng1 - Initial bearing from first point.
|
331
|
+
* @param {LatLon} p2 - Second point.
|
332
|
+
* @param {number} brng2 - Initial bearing from second point.
|
333
|
+
* @returns {Object} containing lat/lng information of intersection.
|
334
|
+
*
|
335
|
+
* @example
|
336
|
+
* var p1 = LatLon(51.8853, 0.2545), brng1 = 108.55;
|
337
|
+
* var p2 = LatLon(49.0034, 2.5735), brng2 = 32.44;
|
338
|
+
* var pInt = LatLon.intersection(p1, brng1, p2, brng2); // pInt.toString(): 50.9078°N, 4.5084°E
|
339
|
+
*/
|
340
|
+
_intersection : function(p1, brng1, p2, brng2) {
|
341
|
+
// see http://williams.best.vwh.net/avform.htm#Intersection
|
342
|
+
|
343
|
+
var φ1 = p1.lat.toRadians(), λ1 = p1.lng.toRadians();
|
344
|
+
var φ2 = p2.lat.toRadians(), λ2 = p2.lng.toRadians();
|
345
|
+
var θ13 = Number(brng1).toRadians(), θ23 = Number(brng2).toRadians();
|
346
|
+
var Δφ = φ2-φ1, Δλ = λ2-λ1;
|
347
|
+
|
348
|
+
var δ12 = 2*Math.asin( Math.sqrt( Math.sin(Δφ/2)*Math.sin(Δφ/2) +
|
349
|
+
Math.cos(φ1)*Math.cos(φ2)*Math.sin(Δλ/2)*Math.sin(Δλ/2) ) );
|
350
|
+
if (δ12 == 0) return null;
|
351
|
+
|
352
|
+
// initial/final bearings between points
|
353
|
+
var θ1 = Math.acos( ( Math.sin(φ2) - Math.sin(φ1)*Math.cos(δ12) ) /
|
354
|
+
( Math.sin(δ12)*Math.cos(φ1) ) );
|
355
|
+
if (isNaN(θ1)) θ1 = 0; // protect against rounding
|
356
|
+
var θ2 = Math.acos( ( Math.sin(φ1) - Math.sin(φ2)*Math.cos(δ12) ) /
|
357
|
+
( Math.sin(δ12)*Math.cos(φ2) ) );
|
358
|
+
|
359
|
+
if (Math.sin(λ2-λ1) > 0) {
|
360
|
+
var θ12 = θ1;
|
361
|
+
var θ21 = 2*Math.PI - θ2;
|
362
|
+
} else {
|
363
|
+
var θ12 = 2*Math.PI - θ1;
|
364
|
+
var θ21 = θ2;
|
365
|
+
}
|
366
|
+
|
367
|
+
var α1 = (θ13 - θ12 + Math.PI) % (2*Math.PI) - Math.PI; // angle 2-1-3
|
368
|
+
var α2 = (θ21 - θ23 + Math.PI) % (2*Math.PI) - Math.PI; // angle 1-2-3
|
369
|
+
|
370
|
+
if (Math.sin(α1)==0 && Math.sin(α2)==0) return null; // infinite intersections
|
371
|
+
if (Math.sin(α1)*Math.sin(α2) < 0) return null; // ambiguous intersection
|
372
|
+
|
373
|
+
//α1 = Math.abs(α1);
|
374
|
+
//α2 = Math.abs(α2);
|
375
|
+
// ... Ed Williams takes abs of α1/α2, but seems to break calculation?
|
376
|
+
|
377
|
+
var α3 = Math.acos( -Math.cos(α1)*Math.cos(α2) +
|
378
|
+
Math.sin(α1)*Math.sin(α2)*Math.cos(δ12) );
|
379
|
+
var δ13 = Math.atan2( Math.sin(δ12)*Math.sin(α1)*Math.sin(α2),
|
380
|
+
Math.cos(α2)+Math.cos(α1)*Math.cos(α3) )
|
381
|
+
var φ3 = Math.asin( Math.sin(φ1)*Math.cos(δ13) +
|
382
|
+
Math.cos(φ1)*Math.sin(δ13)*Math.cos(θ13) );
|
383
|
+
var Δλ13 = Math.atan2( Math.sin(θ13)*Math.sin(δ13)*Math.cos(φ1),
|
384
|
+
Math.cos(δ13)-Math.sin(φ1)*Math.sin(φ3) );
|
385
|
+
var λ3 = λ1 + Δλ13;
|
386
|
+
λ3 = (λ3+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..+180º
|
387
|
+
|
388
|
+
return {lat: φ3.toDegrees(),
|
389
|
+
lng: λ3.toDegrees()
|
390
|
+
};
|
391
|
+
},
|
392
|
+
|
393
|
+
/**
|
394
|
+
* Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
|
395
|
+
* @param obj1
|
396
|
+
* @param obj2
|
397
|
+
* @returns obj3 a new object based on obj1 and obj2
|
398
|
+
*/
|
399
|
+
_merge_options: function(obj1,obj2){
|
400
|
+
var obj3 = {};
|
401
|
+
for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
|
402
|
+
for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
|
403
|
+
return obj3;
|
404
|
+
}
|
405
|
+
});
|
406
|
+
|
407
|
+
L.geodesic = function(latlngs, options) {
|
408
|
+
return new L.Geodesic(latlngs, options);
|
409
|
+
};
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: leaflet-geodesic-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Volker Wiegand
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: railties
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.1'
|
27
|
+
description: Rails engine for the Leaflet.Geodesic code
|
28
|
+
email:
|
29
|
+
- volker.wiegand@cvw.de
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- LICENSE.txt
|
35
|
+
- README.md
|
36
|
+
- lib/leaflet/geodesic/rails.rb
|
37
|
+
- lib/leaflet/geodesic/rails/version.rb
|
38
|
+
- vendor/assets/javascripts/leaflet.geodesic.js
|
39
|
+
homepage: https://github.com/volkerwiegand/leaflet-geodesic-rails
|
40
|
+
licenses:
|
41
|
+
- MIT
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.4.2
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: Add-on to draw geodesic lines with Leaflet.
|
63
|
+
test_files: []
|