Merge pull request #2266 from bhousel/bhousel-scale

Add linear map scale to footer section
This commit is contained in:
John Firebaugh
2014-06-25 12:23:53 -07:00
9 changed files with 275 additions and 50 deletions
+45 -21
View File
@@ -2181,7 +2181,8 @@ img.wiki-image {
/* About Section
------------------------------------------------------- */
.about-block {
#footer {
width: 100%;
position: absolute;
right:0;
bottom:0;
@@ -2192,13 +2193,47 @@ img.wiki-image {
transition: opacity 200ms;
}
.about-block:hover {
#footer:hover {
opacity: 1;
}
#about {
#scale-block {
display: table-cell;
vertical-align: bottom;
width: 250px;
height: 30px;
float: left;
clear: left;
}
#info-block {
float: right;
clear: right;
}
#scale {
height: 30px;
width: 100%;
}
#scale text {
font: 12px sans-serif;
stroke: none;
fill: #ccc;
text-anchor: start;
}
#scale path {
fill: none;
stroke: #ccc;
stroke-width: 1;
shape-rendering: crispEdges;
}
#about-list {
text-align: right;
margin-right: 10px;
clear: right;
}
.source-switch a {
@@ -2240,29 +2275,18 @@ img.wiki-image {
content: ', ';
}
/* API Status */
.api-status {
float: left;
float: right;
clear: both;
text-align: right;
width: 100%;
}
.api-status.offline,
.api-status.readonly {
.api-status.readonly,
.api-status.error {
background: red;
padding: 5px 10px;
}
/* Account Information */
.account {
float: left;
padding: 5px 10px;
}
.account .logout {
margin-left:10px;
border-left: 1px solid white;
padding-left: 10px;
padding: 0px 5px;
}
/* Modals
+1
View File
@@ -90,6 +90,7 @@
<script src='js/id/ui/notice.js'></script>
<script src='js/id/ui/flash.js'></script>
<script src='js/id/ui/save.js'></script>
<script src='js/id/ui/scale.js'></script>
<script src='js/id/ui/splash.js'></script>
<script src='js/id/ui/spinner.js'></script>
<script src='js/id/ui/restore.js'></script>
+30 -3
View File
@@ -21,11 +21,38 @@ iD.geo.euclideanDistance = function(a, b) {
var x = a[0] - b[0], y = a[1] - b[1];
return Math.sqrt((x * x) + (y * y));
};
// using WGS84 polar radius (6356752.314245179 m)
// const = 2 * PI * r / 360
iD.geo.latToMeters = function(dLat) {
return dLat * 110946.257617;
};
// using WGS84 equatorial radius (6378137.0 m)
// const = 2 * PI * r / 360
iD.geo.lonToMeters = function(dLon, atLat) {
return Math.abs(atLat) >= 90 ? 0 :
dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
};
// using WGS84 polar radius (6356752.314245179 m)
// const = 2 * PI * r / 360
iD.geo.metersToLat = function(m) {
return m / 110946.257617;
};
// using WGS84 equatorial radius (6378137.0 m)
// const = 2 * PI * r / 360
iD.geo.metersToLon = function(m, atLat) {
return Math.abs(atLat) >= 90 ? 0 :
m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
};
// Equirectangular approximation of spherical distances on Earth
iD.geo.sphericalDistance = function(a, b) {
var x = Math.cos(a[1]*Math.PI/180) * (a[0] - b[0]),
y = a[1] - b[1];
return 6.3710E6 * Math.sqrt((x * x) + (y * y)) * Math.PI/180;
var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
y = iD.geo.latToMeters(a[1] - b[1]);
return Math.sqrt((x * x) + (y * y));
};
iD.geo.edgeEqual = function(a, b) {
+2 -2
View File
@@ -58,8 +58,8 @@ _.extend(iD.geo.Extent.prototype, {
},
padByMeters: function(meters) {
var dLat = meters / 111200,
dLon = meters / 111200 / Math.abs(Math.cos(this.center()[1]));
var dLat = iD.geo.metersToLat(meters),
dLon = iD.geo.metersToLon(meters, this.center()[1]);
return iD.geo.Extent(
[this[0][0] - dLon, this[0][1] - dLat],
[this[1][0] + dLon, this[1][1] + dLat]);
+17 -12
View File
@@ -80,23 +80,24 @@ iD.ui = function(context) {
.attr('class', 'map-control help-control')
.call(iD.ui.Help(context));
var about = content.append('div')
.attr('class','col12 about-block fillD');
var footer = content.append('div')
.attr('id', 'footer')
.attr('class', 'fillD');
about.append('div')
.attr('class', 'api-status')
.call(iD.ui.Status(context));
footer.append('div')
.attr('id', 'scale-block')
.call(iD.ui.Scale(context));
var linkList = footer.append('div')
.attr('id', 'info-block')
.append('ul')
.attr('id', 'about-list')
.attr('class', 'link-list');
if (!context.embed()) {
about.append('div')
.attr('class', 'account')
.call(iD.ui.Account(context));
linkList.call(iD.ui.Account(context));
}
var linkList = about.append('ul')
.attr('id', 'about')
.attr('class', 'link-list');
linkList.append('li')
.append('a')
.attr('target', '_blank')
@@ -123,6 +124,10 @@ iD.ui = function(context) {
.attr('tabindex', -1)
.call(iD.ui.Contributors(context));
footer.append('div')
.attr('class', 'api-status')
.call(iD.ui.Status(context));
window.onbeforeunload = function() {
return context.save();
};
+20 -7
View File
@@ -3,20 +3,25 @@ iD.ui.Account = function(context) {
function update(selection) {
if (!connection.authenticated()) {
selection.html('')
selection.selectAll('#userLink, #logoutLink')
.style('display', 'none');
return;
}
selection.style('display', 'block');
connection.userDetails(function(err, details) {
selection.html('');
var userLink = selection.select('#userLink'),
logoutLink = selection.select('#logoutLink');
userLink.html('');
logoutLink.html('');
if (err) return;
selection.selectAll('#userLink, #logoutLink')
.style('display', 'list-item');
// Link
var userLink = selection.append('a')
userLink.append('a')
.attr('href', connection.userURL(details.display_name))
.attr('target', '_blank');
@@ -35,7 +40,7 @@ iD.ui.Account = function(context) {
.attr('class', 'label')
.text(details.display_name);
selection.append('a')
logoutLink.append('a')
.attr('class', 'logout')
.attr('href', '#')
.text(t('logout'))
@@ -47,7 +52,15 @@ iD.ui.Account = function(context) {
}
return function(selection) {
connection.on('auth', function() { update(selection); });
selection.append('li')
.attr('id', 'logoutLink')
.style('display', 'none');
selection.append('li')
.attr('id', 'userLink')
.style('display', 'none');
connection.on('auth.account', function() { update(selection); });
update(selection);
};
};
+82
View File
@@ -0,0 +1,82 @@
iD.ui.Scale = function(context) {
var projection = context.projection,
imperial = (iD.detect().locale === 'en-us'),
maxLength = 180,
tickHeight = 8;
function scaleDefs(loc1, loc2) {
var lat = (loc2[1] + loc1[1]) / 2,
conversion = (imperial ? 3.28084 : 1),
dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
scale = { dist: 0, px: 0, text: '' },
buckets, i, val, dLon;
if (imperial) {
buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
} else {
buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
}
// determine a user-friendly endpoint for the scale
for (i = 0; i < buckets.length; i++) {
val = buckets[i];
if (dist >= val) {
scale.dist = Math.floor(dist / val) * val;
break;
}
}
dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
if (imperial) {
if (scale.dist >= 5280) {
scale.dist /= 5280;
scale.text = String(scale.dist) + ' mi';
} else {
scale.text = String(scale.dist) + ' ft';
}
} else {
if (scale.dist >= 1000) {
scale.dist /= 1000;
scale.text = String(scale.dist) + ' km';
} else {
scale.text = String(scale.dist) + ' m';
}
}
return scale;
}
function update(selection) {
// choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
var dims = context.map().dimensions(),
loc1 = projection.invert([0, dims[1]]),
loc2 = projection.invert([maxLength, dims[1]]),
scale = scaleDefs(loc1, loc2);
selection.select('#scalepath')
.attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
selection.select('#scaletext')
.attr('x', scale.px + 8)
.attr('y', tickHeight)
.text(scale.text);
}
return function(selection) {
var g = selection.append('svg')
.attr('id', 'scale')
.append('g')
.attr('transform', 'translate(10,11)');
g.append('path').attr('id', 'scalepath');
g.append('text').attr('id', 'scaletext');
update(selection);
context.map().on('move.scale', function() {
update(selection);
});
};
};
+1
View File
@@ -85,6 +85,7 @@
<script src='../js/id/ui/notice.js'></script>
<script src='../js/id/ui/flash.js'></script>
<script src='../js/id/ui/save.js'></script>
<script src='../js/id/ui/scale.js'></script>
<script src='../js/id/ui/splash.js'></script>
<script src='../js/id/ui/restore.js'></script>
<script src='../js/id/ui/tag_reference.js'></script>
+77 -5
View File
@@ -57,6 +57,78 @@ describe('iD.geo', function() {
});
});
describe('.latToMeters', function() {
it('0 degrees latitude is 0 meters', function() {
expect(iD.geo.latToMeters(0)).to.eql(0);
});
it('1 degree latitude is approx 111 km', function() {
expect(iD.geo.latToMeters(1)).to.be.within(110E3, 112E3);
});
it('-1 degree latitude is approx -111 km', function() {
expect(iD.geo.latToMeters(-1)).to.be.within(-112E3, -110E3);
});
});
describe('.lonToMeters', function() {
it('0 degrees longitude is 0 km', function() {
expect(iD.geo.lonToMeters(0, 0)).to.eql(0);
});
it('distance of 1 degree longitude varies with latitude', function() {
expect(iD.geo.lonToMeters(1, 0)).to.be.within(110E3, 112E3);
expect(iD.geo.lonToMeters(1, 15)).to.be.within(107E3, 108E3);
expect(iD.geo.lonToMeters(1, 30)).to.be.within(96E3, 97E3);
expect(iD.geo.lonToMeters(1, 45)).to.be.within(78E3, 79E3);
expect(iD.geo.lonToMeters(1, 60)).to.be.within(55E3, 56E3);
expect(iD.geo.lonToMeters(1, 75)).to.be.within(28E3, 29E3);
expect(iD.geo.lonToMeters(1, 90)).to.eql(0);
});
it('distance of -1 degree longitude varies with latitude', function() {
expect(iD.geo.lonToMeters(-1, 0)).to.be.within(-112E3, -110E3);
expect(iD.geo.lonToMeters(-1, -15)).to.be.within(-108E3, -107E3);
expect(iD.geo.lonToMeters(-1, -30)).to.be.within(-97E3, -96E3);
expect(iD.geo.lonToMeters(-1, -45)).to.be.within(-79E3, -78E3);
expect(iD.geo.lonToMeters(-1, -60)).to.be.within(-56E3, -55E3);
expect(iD.geo.lonToMeters(-1, -75)).to.be.within(-29E3, -28E3);
expect(iD.geo.lonToMeters(-1, -90)).to.eql(0);
});
});
describe('.metersToLat', function() {
it('0 meters is 0 degrees latitude', function() {
expect(iD.geo.metersToLat(0)).to.eql(0);
});
it('111 km is approx 1 degree latitude', function() {
expect(iD.geo.metersToLat(111E3)).to.be.within(0.995, 1.005);
});
it('-111 km is approx -1 degree latitude', function() {
expect(iD.geo.metersToLat(-111E3)).to.be.within(-1.005, -0.995);
});
});
describe('.metersToLon', function() {
it('0 meters is 0 degrees longitude', function() {
expect(iD.geo.metersToLon(0, 0)).to.eql(0);
});
it('distance of 1 degree longitude varies with latitude', function() {
expect(iD.geo.metersToLon(111320, 0)).to.be.within(0.995, 1.005);
expect(iD.geo.metersToLon(107551, 15)).to.be.within(0.995, 1.005);
expect(iD.geo.metersToLon(96486, 30)).to.be.within(0.995, 1.005);
expect(iD.geo.metersToLon(78847, 45)).to.be.within(0.995, 1.005);
expect(iD.geo.metersToLon(55800, 60)).to.be.within(0.995, 1.005);
expect(iD.geo.metersToLon(28902, 75)).to.be.within(0.995, 1.005);
expect(iD.geo.metersToLon(1, 90)).to.eql(0);
});
it('distance of -1 degree longitude varies with latitude', function() {
expect(iD.geo.metersToLon(-111320, 0)).to.be.within(-1.005, -0.995);
expect(iD.geo.metersToLon(-107551, 15)).to.be.within(-1.005, -0.995);
expect(iD.geo.metersToLon(-96486, 30)).to.be.within(-1.005, -0.995);
expect(iD.geo.metersToLon(-78847, 45)).to.be.within(-1.005, -0.995);
expect(iD.geo.metersToLon(-55800, 60)).to.be.within(-1.005, -0.995);
expect(iD.geo.metersToLon(-28902, 75)).to.be.within(-1.005, -0.995);
expect(iD.geo.metersToLon(-1, 90)).to.eql(0);
});
});
describe('.sphericalDistance', function() {
it('distance between two same points is zero', function() {
var a = [0, 0],
@@ -66,22 +138,22 @@ describe('iD.geo', function() {
it('a straight 1 degree line at the equator is aproximately 111 km', function() {
var a = [0, 0],
b = [1, 0];
expect(iD.geo.sphericalDistance(a, b)).to.be.within(100E3,120E3);
expect(iD.geo.sphericalDistance(a, b)).to.be.within(110E3, 112E3);
});
it('a pythagorean triangle is right', function() {
it('a pythagorean triangle is (nearly) right', function() {
var a = [0, 0],
b = [4, 3];
expect(iD.geo.sphericalDistance(a, b)).to.be.within(500E3,600E3);
expect(iD.geo.sphericalDistance(a, b)).to.be.within(555E3, 556E3);
});
it('east-west distances at high latitude are shorter', function() {
var a = [0, 60],
b = [1, 60];
expect(iD.geo.sphericalDistance(a, b)).to.be.within(50E3,60E3);
expect(iD.geo.sphericalDistance(a, b)).to.be.within(55E3, 56E3);
});
it('north-south distances at high latitude are not shorter', function() {
var a = [0, 60],
b = [0, 61];
expect(iD.geo.sphericalDistance(a, b)).to.be.within(100E3,120E3);
expect(iD.geo.sphericalDistance(a, b)).to.be.within(110E3, 112E3);
});
});