leaflet-geodesic-rails 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|