Skip to content

Instantly share code, notes, and snippets.

@andrewharvey
Created September 4, 2011 11:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewharvey/1192690 to your computer and use it in GitHub Desktop.
Save andrewharvey/1192690 to your computer and use it in GitHub Desktop.
A web map based on Polymaps delivering a 3D experience for viewing NearMap aerial imagery
<!--
This page licensed CC0 http://creativecommons.org/publicdomain/zero/1.0/
Some of the javascript code based of examples at http://polymaps.org/ex/
This is just a prototype/technology test. It probably isn't the cleanest solution...
TODO:
* Ctrl + click and drag should rotate the map just like the keys do
* add perspective option
-->
<html>
<head>
<!-- We are using a patched polymaps.js, which can be generated by taking
http://github.com/simplegeo/polymaps/zipball/v2.5.0 and applying ./Layer.diff
to src/Layer.js, then bulding again.
-->
<script type="text/javascript" src="/javascript/polymaps/polymaps.min.js"></script>
<style type="text/css">
html, body {
height: 100%;
}
body {
margin: 0;
background: #E5E0D9;
}
svg {
display: block;
overflow: hidden;
width: 100%;
height: 100%;
}
.text-strip {
position: absolute;
background: white;
opacity: 0.8;
font-size: small;
box-shadow: 0 0 5px white;
}
#copyright {
right: 0;
bottom: 0;
}
#view-status {
left: 0;
bottom: 0;
}
</style>
</head>
<body id="map">
<div id="view-status" class="text-strip">&#8596; 0&#176; &#8597;1 <b>&#8593;</b>Vert</div>
<div id="copyright" class="text-strip">Aerial Imagery &copy; <a href="http://www.nearmap.com/">NearMap</a>, used under their <a href="http://www.nearmap.com/products/community-licence">Community License</a></div>
<script type="text/javascript">
// the polymaps object
var po = org.polymaps;
// tweak these to control how fine grain movements are
var pitch_step = 0.05;
var spin_step = (1/32)*Math.PI;
// set up the SVG containers for the map, we need them so we can apply transformations to them
var div = document.getElementById("map"),
svg = div.appendChild(po.svg("svg")),
g = svg.appendChild(po.svg("g"));
// define the map layers
var nearmap_tile_url = "http://www.nearmap.com/maps/z={Z}&x={X}&y={Y}&nml=";
var vert = po.image().url(po.url(nearmap_tile_url + "Vert"));
var north = po.image().url(po.url(nearmap_tile_url + "N__"));
var south = po.image().url(po.url(nearmap_tile_url + "S__")).tileTransform("rotate(180) translate(-256 -256)");
var east = po.image().url(po.url(nearmap_tile_url + "E__")).tileTransform("rotate(90) translate(0 -256)");
var west = po.image().url(po.url(nearmap_tile_url + "W__")).tileTransform("rotate(270) translate(-256 0)");
var currentLayer = vert;
// set up the map
var map = po.map()
.container(g)
.add(currentLayer)
.add(po.interact())
.center({lat: -33.852098, lon: 151.210670})
.zoomRange([10, 24])
.zoom(17);
// rot is longitude of the view (0 is looking north, PI is looking south, PI/2 is looking west, -PI/2 is looking east)
var rot = 0;
// pitch is latitude, 1 is looking straight down, 0 is flat across the plane, 0.5 is looking at 45 degrees
var pitch = 1;
// which keys were pressed down?
var key = {left: 0, right: 0, up: 0, down: 0};
// tweak these to control the camera movement speed
var repeatTimer,
repeatDelay = 250,
repeatInterval = 50;
function keydown(e) {
if (e.type != "keydown" || e.ctrlKey || e.altKey || e.metaKey) return;
switch (e.keyCode) {
case 65: // left
key.left = 1;
break;
case 69: // right
key.right = 1;
break;
case 188: // up
key.up = 1;
break;
case 79: // down
key.down = 1;
break;
}
// move the camera based on what keys are pressed
updateView();
if (!repeatTimer && (key.left | key.right | key.up | key.down)) {
repeatTimer = setInterval(updateView, repeatInterval);
}
e.preventDefault();
}
function keyup(e) {
switch (e.keyCode) {
case 65: key.left = 0; break;
case 69: key.right = 0; break;
case 188: key.up = 0; break;
case 79: key.down = 0; break;
default: return;
}
if (repeatTimer && !(key.left | key.right | key.up | key.down)) {
repeatTimer = clearInterval(repeatTimer);
}
e.preventDefault();
}
// change the current map layer to newLayer
function changeMapLayer(newLayer) {
map.remove(currentLayer);
currentLayer = newLayer;
map.add(currentLayer);
}
// move the camera based key status
function updateView() {
if (!map) return;
// set new rot and pitch values
if (key.right)
rot += spin_step;
if (key.left)
rot += -spin_step;
if (key.up)
pitch += pitch_step;
if (key.down)
pitch -= pitch_step;
// set limits on the pitch
if (pitch > 1)
pitch = 1;
if (pitch < 0.5)
pitch = 0.5;
// cap the rot between -PI and PI
rot = ((rot + Math.PI) % (2*Math.PI)) - Math.PI;
if (rot < -Math.PI)
rot += 2*Math.PI;
var cx = map.size().x / 2;
var cy = map.size().y / 2;
// change the layer based on the current pitch
if (pitch < 0.8) {
// change the layer based on the current rot
if ((rot <= (Math.PI/4)) && (rot >= -(Math.PI/4)) && currentLayer != north) {
changeMapLayer(north);
}else if ((rot < (3*Math.PI/4)) && (rot > (Math.PI/4)) && currentLayer != west) {
changeMapLayer(west);
}else if ((rot >= (3*Math.PI/4)) || (rot <= (-3*Math.PI/4)) && currentLayer != south) {
changeMapLayer(south);
}else if ((rot < (-Math.PI/4)) && (rot > (-3*Math.PI/4)) && currentLayer != east) {
changeMapLayer(east);
}
}
if ((pitch >= 0.8) && (currentLayer != vert)) {
changeMapLayer(vert)
}
// fill the info bar with details of the current view
var layerName;
if (currentLayer == vert)
layerName = "Vert";
if (currentLayer == north)
layerName = "North";
if (currentLayer == south)
layerName = "South";
if (currentLayer == east)
layerName = "East";
if (currentLayer == west)
layerName = "West";
document.getElementById("view-status").innerHTML = "&#8596;" + (rot*180/Math.PI).toFixed(0) + "&#176; &#8597;" + pitch.toFixed(2) + " <b>&#8593;</b>" + layerName;
// rotate the map
if (rot) map.angle(rot);
/*
* transform attribute
* eg. transform="translate(10,10) scale(1 2)"
*
* matrix(<a> <b> <c> <d> <e> <f>)
* translate(<tx> [<ty>])
* scale(<sx> [<sy>])
* rotate(<rotate-angle> [<cx> <cy>])
* ...with angle in degrees
* skewX(<skew-angle>)
* skewY(<skew-angle>)
*/
g.setAttribute("transform",
"translate(" + cx + " " + cy + ") " +
"scale(1 " + pitch + ") " +
"translate(" + -cx + " " + -cy + ")");
}
window.addEventListener("keydown", keydown, true);
window.addEventListener("keyup", keyup, true);
</script>
</body>
</html>
--- Layer.js 2011-04-10 21:10:40.000000000 +1000
+++ Layer.js 2011-09-04 21:11:11.371804701 +1000
@@ -8,6 +8,7 @@
map,
container = po.svg("g"),
transform,
+ tileTransform,
levelZoom,
levels = {};
@@ -210,9 +211,11 @@
for (var key in newLocks) {
var t = newLocks[key],
k = Math.pow(2, t.level = t.zoom - tileCenter.zoom);
+ if (tileTransform == undefined)
+ tileTransform = "";
t.element.setAttribute("transform", "translate("
+ (t.x = tileSize.x * (t.column - tileCenter.column * k)) + ","
- + (t.y = tileSize.y * (t.row - tileCenter.row * k)) + ")");
+ + (t.y = tileSize.y * (t.row - tileCenter.row * k)) + ") " + tileTransform);
}
// remove tiles that are no longer visible
@@ -302,6 +305,13 @@
if (map) move();
return layer;
};
+
+ layer.tileTransform = function(x) {
+ if (!arguments.length) return tileTransform;
+ tileTransform = x;
+ if (map) move();
+ return layer;
+ };
layer.zoom = function(x) {
if (!arguments.length) return zoom;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment