Playing around with the idea of snapping to a latlon grid, by rounding the latlon values of my mouse coordinates to 1 decimal place.
Not exactly sure how this will be useful, but it could be an interesting way to interact with a values binned to a grid on the globe.
Built with blockbuilder.org
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-geo-projection/0.2.9/d3.geo.projection.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script src="trackball.js"></script>
<style>
body {
margin:0;position:fixed;top:0;right:0;bottom:0;left:0;
background-color: #111;
}
svg { width: 100%; height: 100%; }
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.land {
fill: #045728;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
circle {
fill: #93fff4;
pointer-events: none;
}
</style>
</head>
<body>
<svg></svg>
<script>
var width = 960;
var height = 500;
var svg = d3.select("svg");
var scale = (width - 1) / 2 / Math.PI * 3
var projection = d3.geo.orthographic()
.scale(scale)
.translate([width / 2, height / 2])
.clipAngle(90)
.precision(.1);
var zoom = d3.behavior.zoom()
.translate([width / 2, height / 2])
.scale(scale)
.scaleExtent([scale, 8 * scale])
.on("zoom", zoomed)
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
d3.json("world-110m.json", function(err, world) {
svg.append("path")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
svg.append("path")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
})
svg.call(zoom)
.call(zoom.event);
function zoomed() {
projection
.scale(zoom.scale())
update(projection.rotate());
}
d3.behavior.trackball(svg).on("rotate", update);
function update(rot) {
//update the rotation in our projection
projection.rotate(rot);
d3.selectAll("path")
.attr("d", path);
//console.log("updating circles")
makePoints();
}
svg.on("mousemove.circles", makePoints);
function makePoints() {
var xy = d3.mouse(svg.node());
var latlon = projection.invert(xy)
if(isNaN(latlon[0]) || isNaN(latlon[1])) {
return;
}
//console.log("xy", xy, "latlon", latlon)
//rounding to degree (whole numbers)
var p = [round(latlon[0]), round(latlon[1])];
var points = generateSquare(p[0], p[1], 20, 1);
console.log("points", points[0])
var rxy = projection(p);
var circles = svg.selectAll("circle").data(points)
circles.enter().append("circle")
circles
.attr({
r: function(d) { return 3 },
"fill-opacity": function(d) { return 1-d.d/10 },
cx: function(d) { return projection([d.x, d.y])[0] },
cy: function(d) { return projection([d.x, d.y])[1] }
})
}
function generateSquare(cx, cy, width, step) {
//generate points that fill in a square around a center
var nside = Math.floor(width / step + 0.5); //rounded
var points = [];
for(var i = 0; i < nside; i++) {
for(var j = 0; j < nside; j++) {
var x = cx - width/2 + i * step;
var y = cy - width/2 + j * step;
points.push({
x: x,
y: y,
d: Math.sqrt((cx-x)*(cx-x) + (cy-y)*(cy-y))
})
}
}
return points;
}
function round(deg, decimals) {
if(!decimals) decimals = 0;
var factor = Math.pow(10, decimals);
return Math.floor(deg * factor + 0.5)/factor;
}
//hover over space in the globe, see circles at rounded locations
// transition in size
console.log(round(77.213, 2))
//place rounded navigator.geolocation on globe using pubnub
navigator.geolocation.getCurrentPosition(function(position) {
// this doesn't seem to work in blockbuilder.org because of the
// iframe sandboxing
console.log("position", position);
});
</script>
</body>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-geo-projection/0.2.9/d3.geo.projection.min.js
https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js