<!DOCTYPE html>
<html ng-app="geodesicApp">
<head>
<script data-require="angular.js@*" data-semver="1.2.0-rc3-nonmin" src="//code.angularjs.org/1.2.0-rc.3/angular.js"></script>
<script src="geodesic.js"></script>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.0/css/font-awesome.css">
<link rel="stylesheet" href="style.css" />
</head>
<body ng-controller="MainCtrl" ng-init="init()">
<!-- Inspired from: http://tympanus.net/Tutorials/Cust"omDropDownListStyling/index3.html -->
<div id="dd" class="wrapper-dropdown-3" ng-class="{'active':dropDownEnabled}" ng-click="dropDownEnabled = !dropDownEnabled">
<span>Select</span>
<ul class="dropdown">
<li ng-repeat="site in frenchSites | orderBy:'rawDistance'">
<a href="#"><i class="fa fa-map-marker icon-large"></i>{{ site.name }} <span><strong>{{ site.distance }}</strong></span></a>
</li>
</ul>
</div>
<script src="controller.js"></script>
<!-- Always enjoyable to be flttred! -->
<div style="position:absolute;bottom:0px; margin: 0px 0px 40px 10px;">
<script id='fbketsn'>(function(i){var f,s=document.getElementById(i);f=document.createElement('iframe');f.src='//api.flattr.com/button/view/?uid=ggarcia&button=compact&url='+encodeURIComponent(document.URL);f.title='Flattr';f.height=20;f.width=110;f.style.borderWidth=0;s.parentNode.insertBefore(f,s);})('fbketsn');</script>
</div>
</body>
</html>
/* Inspired from: http://tympanus.net/Tutorials/CustomDropDownListStyling/index3.html */
/* DEMO 3 */
.wrapper-dropdown-3 {
/* Size and position */
position: relative;
width: 200px;
margin: 0 auto;
padding: 10px;
/* Styles */
background: #fff;
border-radius: 7px;
border: 1px solid rgba(0,0,0,0.15);
box-shadow: 0 1px 1px rgba(50,50,50,0.1);
cursor: pointer;
outline: none;
/* Font settings */
font-weight: bold;
color: #8AA8BD;
}
.wrapper-dropdown-3:after {
content: "";
width: 0;
height: 0;
position: absolute;
right: 15px;
top: 50%;
margin-top: -3px;
border-width: 6px 6px 0 6px;
border-style: solid;
border-color: #8aa8bd transparent;
}
.wrapper-dropdown-3 .dropdown {
/* Size & position */
position: absolute;
top: 140%;
left: -80px;
right: 0;
width: 300px;
/* Styles */
background: white;
border-radius: inherit;
border: 1px solid rgba(0,0,0,0.17);
box-shadow: 0 0 5px rgba(0,0,0,0.1);
font-weight: normal;
-webkit-transition: all 0.5s ease-in;
-moz-transition: all 0.5s ease-in;
-ms-transition: all 0.5s ease-in;
-o-transition: all 0.5s ease-in;
transition: all 0.5s ease-in;
list-style: none;
padding: 0px 0px 0px 0px;
/* Hiding */
opacity: 0;
pointer-events: none;
}
.wrapper-dropdown-3 .dropdown:after {
content: "";
width: 0;
height: 0;
position: absolute;
bottom: 100%;
right: 15px;
border-width: 0 6px 6px 6px;
border-style: solid;
border-color: #fff transparent;
}
.wrapper-dropdown-3 .dropdown:before {
content: "";
width: 0;
height: 0;
position: absolute;
bottom: 100%;
right: 13px;
border-width: 0 8px 8px 8px;
border-style: solid;
border-color: rgba(0,0,0,0.1) transparent;
}
.wrapper-dropdown-3 .dropdown li a {
display: block;
padding: 10px;
text-decoration: none;
color: #8aa8bd;
border-bottom: 1px solid #e6e8ea;
box-shadow: inset 0 1px 0 rgba(255,255,255,1);
-webkit-transition: all 0.3s ease-out;
-moz-transition: all 0.3s ease-out;
-ms-transition: all 0.3s ease-out;
-o-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
.wrapper-dropdown-3 .dropdown li a span {
float: right;
color: #999;
}
.wrapper-dropdown-3 .dropdown li i {
float: left;
padding: 0px 10px 0px 5px;
color: inherit;
}
.wrapper-dropdown-3 .dropdown li:first-of-type a {
border-radius: 7px 7px 0 0;
}
.wrapper-dropdown-3 .dropdown li:last-of-type a {
border: none;
border-radius: 0 0 7px 7px;
}
/* Hover state */
.wrapper-dropdown-3 .dropdown li:hover a {
background: #f3f8f8;
}
/* Active state */
.wrapper-dropdown-3.active .dropdown {
opacity: 1;
pointer-events: auto;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Vincenty Inverse Solution of Geodesics on the Ellipsoid (c) Chris Veness 2002-2012 */
/* */
/* from: Vincenty inverse formula - T Vincenty, "Direct and Inverse Solutions of Geodesics on the */
/* Ellipsoid with application of nested equations", Survey Review, vol XXII no 176, 1975 */
/* http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function distVincentyDeg(lat1, long1, lat2, long2) {
return( distVincenty( degToRad(lat1),
degToRad(long1),
degToRad(lat2),
degToRad(long2))
);
}
function distVincenty(lat1, long1, lat2, long2) {
var a = 6378137, b = 6356752.3142, f = 1/298.257223563; // WGS-84 ellipsiod
var L = long2 - long1;
var U1 = Math.atan((1-f) * Math.tan(lat1));
var U2 = Math.atan((1-f) * Math.tan(lat2));
var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
var lambda = L, lambdaP = 2*Math.PI;
var iterLimit = 20;
while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
(cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
if (sinSigma==0) return 0; // co-incident points
var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
var sigma = Math.atan2(sinSigma, cosSigma);
var sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
var cosSqAlpha = 1 - sinAlpha*sinAlpha;
var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
if (isNaN(cos2SigmaM)) cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6)
var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
lambdaP = lambda;
lambda = L + (1-C) * f * sinAlpha *
(sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
}
if (iterLimit==0) return NaN // formula failed to converge
var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
var s = b*A*(sigma-deltaSigma);
s = s.toFixed(3); // round to 1mm precision
return s;
}
/*
* convert lat/long in degrees to radians, for handling input values
*
* this is very flexible on formats, allowing signed decimal degrees (numeric or text), or
* deg-min-sec suffixed by compass direction (NSEW). A variety of separators are accepted
* (eg 3º 37' 09"W) or fixed-width format without separators (eg 0033709W). Seconds and minutes
* may be omitted. Minimal validation is done.
*/
function degToRad(llDeg) {
if (!isNaN(llDeg)) return llDeg * Math.PI / 180; // signed decimal degrees without NSEW
llDeg = llDeg.replace(/[\s]*$/,''); // strip trailing whitespace
var dir = llDeg.slice(-1).toUpperCase(); // compass dir'n
if (!/[NSEW]/.test(dir)) return NaN; // check for correct compass direction
llDeg = llDeg.slice(0,-1); // and lose it off the end
var dms = llDeg.split(/[\s:,°º′\'″\"]/); // check for separators indicating d/m/s
if (dms[dms.length-1] == '') dms.length--; // trailing separator? (see note below)
switch (dms.length) { // convert to decimal degrees...
case 3: // interpret 3-part result as d/m/s
var deg = dms[0]/1 + dms[1]/60 + dms[2]/3600; break;
case 2: // interpret 2-part result as d/m
var deg = dms[0]/1 + dms[1]/60; break;
case 1: // non-separated format dddmmss
if (/[NS]/.test(dir)) llDeg = '0' + llDeg; // - normalise N/S to 3-digit degrees
var deg = llDeg.slice(0,3)/1 + llDeg.slice(3,5)/60 + llDeg.slice(5)/3600; break;
default: return NaN;
}
if (/[WS]/.test(dir)) deg = -deg; // take west and south as -ve
return deg * Math.PI / 180; // then convert to radians
}
var app = angular.module('geodesicApp', []);
app.controller('MainCtrl', function($scope, $templateCache, $timeout) {
$scope.author = 'Guillaume Garcia';
$scope.frenchSites = [ { name: "Eiffel Tower", coords: { latitude: 48.858087, longitude: 2.294703 }, distance: "" },
{ name: "Notre-Dame Cathedral", coords: { latitude: 48.853146, longitude: 2.349066 }, distance: "" },
{ name: "Alti-TCS Villiers", coords: { latitude: 48.893554, longitude: 2.276270 }, distance: "" },
{ name: "Saint Michael's Mount", coords: { latitude: 48.635702, longitude: -1.511160 }, distance: "" } ];
$scope.position = null;
$scope.distance = '';
$scope.rawDistance = 0;
$scope.linkToGMaps = '';
$scope.dropDownEnabled = true;
$scope.init = function() {
if (navigator.geolocation) {
// L’API est disponible
navigator.geolocation.getCurrentPosition($scope.calculateDistance);
} else {
// Pas de support...
return("Pas de support de géoloc...");
}
};
$scope.calculateDistance = function(position) {
$scope.$apply(function() {
var siteIndex;
$scope.position = position;
console.log($scope.position);
for (siteIndex = 0 ; siteIndex<$scope.frenchSites.length ; siteIndex++) {
$scope.frenchSites[siteIndex].rawDistance = parseInt(distVincentyDeg( $scope.frenchSites[siteIndex].coords.latitude,
$scope.frenchSites[siteIndex].coords.longitude,
$scope.position.coords.latitude,
$scope.position.coords.longitude ), 10);
$scope.frenchSites[siteIndex].distance = $scope.frenchSites[siteIndex].rawDistance < 1000 ? Math.round($scope.frenchSites[siteIndex].rawDistance) + " m" : Math.round($scope.frenchSites[siteIndex].rawDistance / 1000 * 10) / 10 + " km";
}
});
};
});