Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@i09158knct
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save i09158knct/9709568 to your computer and use it in GitHub Desktop.
Save i09158knct/9709568 to your computer and use it in GitHub Desktop.
2014-03-23 3rd
body{margin:0;padding:0;overflow:hidden}
.banner{background-color:rgba(204,204,204,0.333);position:fixed;top:0;left:0}
<div id="container"></div>
<script src="http://jsrun.it/assets/h/r/V/O/hrVOk"></script><!-- d3.js -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r66/three.min.js"></script><!-- three.js -->
<script src="http://jsrun.it/assets/x/8/7/3/x873l"></script><!-- TrackballControl.js -->
<div class="banner">
&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors
</div>
// Generated by CoffeeScript 1.7.1
// Source: https://gist.github.com/i09158knct/9709568#file-source-coffee
var camera, light1, light2, light3, renderer, scene;
window.lonlatToTile = function(lon, lat, zoom) {
var lonDegreesPerTile, numOfTiles, sinLat, tx, ty;
numOfTiles = Math.pow(2, zoom);
lonDegreesPerTile = 360 / numOfTiles;
sinLat = Math.sin(lat * Math.PI / 180);
tx = (lon + 180) / lonDegreesPerTile;
ty = (0.5 + -0.5 * Math.log((1 + sinLat) / (1 - sinLat)) / (2 * Math.PI)) * numOfTiles;
return [Math.floor(tx), Math.floor(ty)];
};
window.tileToLonlat = function(tx, ty, zoom) {
var lat, latRadians, lon, numOfTiles, x, y;
numOfTiles = Math.pow(2, zoom);
x = tx / numOfTiles;
y = ty / numOfTiles;
lon = (x - (1 / 2)) / (1 / 360);
latRadians = (y - (1 / 2)) / -(1 / (2 * Math.PI));
lat = (2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2) / Math.PI * 180;
return [lon, lat];
};
window.tileToBoundary = function(x, y, zoom) {
var p1, p2;
p1 = tileToLonlat(x, y, zoom);
p2 = tileToLonlat(x + 1, y + 1, zoom);
return {
n: p1[1],
w: p1[0],
s: p2[1],
e: p2[0]
};
};
window.midpoint = function(_arg, _arg1) {
var x, x1, x2, y, y1, y2;
x1 = _arg[0], y1 = _arg[1];
x2 = _arg1[0], y2 = _arg1[1];
x = x1 - (x1 - x2) / 2;
y = y1 - (y1 - y2) / 2;
return [x, y];
};
window.centroid = function(boundary) {
var p1, p2;
p1 = [boundary.w, boundary.n];
p2 = [boundary.e, boundary.s];
return midpoint(p1, p2);
};
window.createProjection = function(center) {
return d3.geo.mercator().scale(6.5 * 1000 * 1000).center(center).translate([0, 0]);
};
window.loadOverpassData = function(boundary) {
var baseUrl, convertToAssoc, url;
convertToAssoc = function(rawData) {
var acc;
acc = {
node: {},
way: {},
relation: {}
};
rawData.elements.forEach(function(elem) {
return acc[elem.type][elem.id] = elem;
});
return acc;
};
baseUrl = "http://overpass-api.de/api/interpreter?data=[out:json];\n(\n node({s},{w},{n},{e});\n way(bn);\n);\n(\n ._;\n node(w);\n);\nout;";
url = baseUrl.replace(/\{([swne])\}/g, function(match, key) {
return boundary[key];
});
return $.getJSON(url).then(convertToAssoc);
};
window.createGeoObject = function(project, overpassData) {
var createAndAddLines, createAndAddPolygons, createLine, createPolygon, findMaterialOptions, getNodes, isArea, lonlatToArray, materialOptions, nodeToVec3, nodeToXy, root, yxToVec3;
materialOptions = {
railway: {
platform: {
color: 0x555500,
amount: 10
},
rail: {
color: 0xffff00,
linewidth: 1
}
},
highway: {
pedestrian: {
color: 0x00cccc,
amount: 1
},
primary: {
color: 0xffaa555,
linewidth: 1000
},
secondary: {
color: 0xaa5500,
linewidth: 5
},
residential: {
color: 0xffffff,
linewidth: 2
},
"default": {
color: 0xcccccc,
linewidth: 1
}
},
waterway: {
"default": {
color: 0x0000ff,
linewidth: 10
}
},
amenity: {
school: {
color: 0x00aa00,
amount: 1000
},
theatre: {
color: 0xcc5500,
amount: 50
},
parking: {
color: 0xffffaa,
amount: 1
},
bus_station: {
color: 0xcc0000,
amount: 10
},
"default": {
color: 0xffffff,
amount: 100
}
},
building: {
commercial: {
color: 0x5555cc,
amount: 50
},
yes: {
color: 0x888888,
amount: 25,
vertexColors: THREE.VertexColors
},
"default": {
color: 0xffffff,
amount: 1
}
},
natural: {
wood: {
color: 0x00ff00,
amount: 50
},
water: {
color: 0x0000cc,
amount: 5
},
"default": {
color: 0x00ff00,
amount: 1000
}
},
leisure: {
pitch: {
color: 0xcc5500,
amount: 5
},
golf_course: {
color: 0x00cc55,
amount: 5
},
"default": {
color: 0x00cc55,
amount: 1000
}
},
landuse: {
forest: {
color: 0x00ff00,
amount: 100
},
old_forest: {
color: 0x005500,
amount: 100
},
"default": {
color: 0x005500,
amount: 500
}
}
};
getNodes = function(overpassData, way) {
return way.nodes.map(function(id) {
return overpassData.node[id];
});
};
isArea = function(way) {
var first, last;
first = way.nodes[0];
last = way.nodes[way.nodes.length - 1];
return first === last;
};
lonlatToArray = function(_arg) {
var lat, lon;
lon = _arg.lon, lat = _arg.lat;
return [lon, lat];
};
yxToVec3 = function(_arg) {
var x, y;
x = _arg[0], y = _arg[1];
return new THREE.Vector3(x, y, 0);
};
nodeToXy = function(node) {
return project(lonlatToArray(node));
};
nodeToVec3 = function(node) {
return yxToVec3(nodeToXy(node));
};
createLine = function(way, opts) {
var create, line;
create = (function(_this) {
return function(way) {
var geometry, nodes;
nodes = getNodes(overpassData, way);
geometry = new THREE.Geometry();
geometry.vertices = nodes.map(function(node) {
return nodeToVec3(node);
});
return geometry;
};
})(this);
return line = new THREE.Line(create(way), new THREE.LineBasicMaterial(opts));
};
createPolygon = function(area, opts) {
var create, createShape, polygon;
if (opts == null) {
opts = {
color: 0xffffff,
opacity: 0.8,
transparent: true
};
}
createShape = function(nodes) {
var shape;
shape = new THREE.Shape();
shape.moveTo.apply(shape, nodeToXy(nodes[0]));
nodes.slice(1).forEach((function(_this) {
return function(node) {
return shape.lineTo.apply(shape, nodeToXy(node));
};
})(this));
return shape;
};
create = (function(_this) {
return function(area, opts) {
var geometry, nodes, shape;
nodes = getNodes(overpassData, area);
shape = createShape(nodes);
geometry = shape.extrude(_.defaults(opts, {
amount: 5,
bevelEnabled: false
}));
geometry.computeFaceNormals();
return geometry;
};
})(this);
return polygon = new THREE.Mesh(create(area, opts), new THREE.MeshLambertMaterial(_.defaults(opts, {
side: THREE.BackSide
})));
};
findMaterialOptions = function(tags) {
var category, key, mkeys, tkeys, tvalue, _ref;
if (tags == null) {
tags = {};
}
mkeys = _.keys(materialOptions);
tkeys = _.keys(tags);
key = _.first(_.intersection(mkeys, tkeys));
if (key != null) {
category = materialOptions[key];
tvalue = tags[key];
return (_ref = category[tvalue]) != null ? _ref : category["default"];
} else {
return null;
}
};
createAndAddLines = function(root) {
var ways;
ways = _.filter(overpassData.way, function(way) {
return !isArea(way);
});
return _.each(ways, function(way) {
var opts;
opts = findMaterialOptions(way.tags);
return root.add(createLine(way, opts));
});
};
createAndAddPolygons = function(root) {
var areas;
areas = _.filter(overpassData.way, isArea);
return _.each(areas, function(area) {
var opts;
opts = findMaterialOptions(area.tags);
return root.add(createPolygon(area, opts));
});
};
root = new THREE.Object3D();
root.rotation.x = 90 * Math.PI / 180;
root.scale.z = -1;
createAndAddLines(root);
createAndAddPolygons(root);
return root;
};
window.tileKaruizawa = {
x: 14501,
y: 6414,
z: 14
};
scene = window.scene = null;
renderer = window.renderer = null;
light1 = window.light1 = null;
light2 = window.light2 = null;
light3 = window.light3 = null;
camera = window.camera = null;
$(function() {
var atmospere, boundary, center, loading, project, targetTile;
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);
light1 = new THREE.DirectionalLight(0xffffffff, 1);
light2 = new THREE.DirectionalLight(0xffffffff, 0.3);
light3 = new THREE.DirectionalLight(0xffffffff, 1);
light1.position.set(1000, -10000, 750);
light2.position.set(-1000, -1000, -750);
light3.position.set(1000, 1000, 750);
scene.add(light1);
scene.add(light2);
scene.add(light3);
atmospere = new THREE.Mesh(new THREE.SphereGeometry(20000, 60, 49), new THREE.MeshBasicMaterial({
color: 0x555555
}));
atmospere.scale.x = -1;
scene.add(atmospere);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 40 * 1000);
camera.position.set(-3000, 3000, -3000);
scene.add(camera);
targetTile = window.tileKaruizawa;
boundary = tileToBoundary(targetTile.x, targetTile.y, targetTile.z);
center = centroid(boundary);
project = createProjection(center);
loading = $.when.apply($, [loadOverpassData(boundary)]);
return loading.then(function(overpassData) {
var animate, controls, geoObject;
geoObject = createGeoObject(project, overpassData);
scene.add(geoObject);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.target = geoObject.position.clone();
controls.target.add(new THREE.Vector3(0, 0, 0));
return requestAnimationFrame(animate = function() {
requestAnimationFrame(animate);
controls.update();
return renderer.render(scene, camera);
});
});
});
# https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
window.lonlatToTile = (lon, lat, zoom) ->
numOfTiles = 2 ** zoom
lonDegreesPerTile = 360 / numOfTiles
sinLat = Math.sin(lat * Math.PI / 180)
tx = (lon + 180) / lonDegreesPerTile
ty = (0.5 + -0.5 * Math.log((1 + sinLat) / (1 - sinLat)) / (2 * Math.PI)) * numOfTiles
[Math.floor(tx), Math.floor(ty)]
window.tileToLonlat = (tx, ty, zoom) ->
numOfTiles = 2 ** zoom
x = tx / numOfTiles
y = ty / numOfTiles
lon = (x - (1 / 2)) / (1 / 360)
latRadians = (y - (1 / 2)) / -(1 / (2 * Math.PI))
lat = (2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2) / Math.PI * 180
[lon, lat]
window.tileToBoundary = (x, y, zoom) ->
p1 = tileToLonlat(x, y, zoom)
p2 = tileToLonlat(x + 1, y + 1, zoom)
n: p1[1]
w: p1[0]
s: p2[1]
e: p2[0]
window.midpoint = ([x1, y1], [x2, y2]) ->
x = x1 - (x1 - x2) / 2
y = y1 - (y1 - y2) / 2
[x, y]
window.centroid = (boundary) ->
p1 = [boundary.w, boundary.n]
p2 = [boundary.e, boundary.s]
midpoint(p1, p2)
window.createProjection = (center) ->
d3.geo.mercator()
.scale(6.5 * 1000 * 1000)
.center(center)
.translate([0, 0])
window.loadOverpassData = (boundary) ->
convertToAssoc = (rawData) ->
acc =
node: {}
way: {}
relation: {}
rawData.elements.forEach (elem) ->
acc[elem.type][elem.id] = elem
acc
baseUrl =
"""
http://overpass-api.de/api/interpreter?data=\
[out:json];
(
node({s},{w},{n},{e});
way(bn);
);
(
._;
node(w);
);
out;
"""
url = baseUrl.replace /\{([swne])\}/g, (match, key) -> boundary[key]
$.getJSON(url)
.then(convertToAssoc)
window.createGeoObject = (project, overpassData) ->
materialOptions =
railway:
platform:
color: 0x555500
amount: 10
rail:
color: 0xffff00
linewidth: 1
highway:
pedestrian:
color: 0x00cccc
amount: 1
primary:
color: 0xffaa555
linewidth: 1000
# fog: true
secondary:
color: 0xaa5500
linewidth: 5
# fog: true
residential:
color: 0xffffff
linewidth: 2
# fog: true
default:
color: 0xcccccc
linewidth: 1
# fog: true
waterway:
default:
color: 0x0000ff
linewidth: 10
# fog: true
amenity:
school:
color: 0x00aa00
amount: 1000
theatre:
color: 0xcc5500
amount: 50
parking:
color: 0xffffaa
amount: 1
bus_station:
color: 0xcc0000
amount: 10
default:
color: 0xffffff
amount: 100
building:
commercial:
color: 0x5555cc
amount: 50
yes:
color: 0x888888
amount: 25
vertexColors: THREE.VertexColors
default:
color: 0xffffff
amount: 1
natural:
wood:
color: 0x00ff00
amount: 50
water:
color: 0x0000cc
amount: 5
default:
color: 0x00ff00
amount: 1000
leisure:
pitch:
color: 0xcc5500
amount: 5
golf_course:
color: 0x00cc55
amount: 5
default:
color: 0x00cc55
amount: 1000
landuse:
forest:
color: 0x00ff00
amount: 100
old_forest:
color: 0x005500
amount: 100
default:
color: 0x005500
amount: 500
getNodes = (overpassData, way) ->
way.nodes.map (id) ->
overpassData.node[id]
isArea = (way) ->
first = way.nodes[0]
last = way.nodes[way.nodes.length - 1]
first == last
lonlatToArray = ({lon, lat}) -> [lon, lat]
yxToVec3 = ([x, y]) -> new THREE.Vector3(x, y, 0)
nodeToXy = (node) -> project(lonlatToArray(node))
nodeToVec3 = (node) -> yxToVec3(nodeToXy(node))
createLine = (way, opts) ->
create = (way) =>
nodes = getNodes(overpassData, way)
geometry = new THREE.Geometry()
geometry.vertices = nodes.map (node) ->
nodeToVec3(node)
geometry
line = new THREE.Line \
create(way),
new THREE.LineBasicMaterial opts
createPolygon = (area, opts={color: 0xffffff, opacity: 0.8, transparent: true}) ->
createShape = (nodes) ->
shape = new THREE.Shape()
shape.moveTo(nodeToXy(nodes[0])...)
nodes.slice(1).forEach (node) =>
shape.lineTo(nodeToXy(node)...)
shape
create = (area, opts) =>
nodes = getNodes(overpassData, area)
shape = createShape(nodes)
geometry = shape.extrude _.defaults opts,
amount: 5
bevelEnabled: false
geometry.computeFaceNormals()
geometry
polygon = new THREE.Mesh \
create(area, opts),
new THREE.MeshLambertMaterial _.defaults opts,
side: THREE.BackSide
findMaterialOptions = (tags={}) ->
mkeys = _.keys(materialOptions)
tkeys = _.keys(tags)
key = _.first(_.intersection(mkeys, tkeys))
if key?
category = materialOptions[key]
tvalue = tags[key]
category[tvalue] ? category.default
else
null
createAndAddLines = (root) ->
ways = _.filter overpassData.way, (way) -> !isArea(way)
_.each ways, (way) ->
opts = findMaterialOptions(way.tags)
root.add(createLine(way, opts))
createAndAddPolygons = (root) ->
areas = _.filter overpassData.way, isArea
_.each areas, (area) ->
opts = findMaterialOptions(area.tags)
root.add(createPolygon(area, opts))
root = new THREE.Object3D()
root.rotation.x = 90 * Math.PI / 180
root.scale.z = -1
createAndAddLines(root)
createAndAddPolygons(root)
root
window.tileKaruizawa =
x: 14501
y: 6414
z: 14
scene = window.scene = null
renderer = window.renderer = null
light1 = window.light1 = null
light2 = window.light2 = null
light3 = window.light3 = null
camera = window.camera = null
$ ->
scene = new THREE.Scene()
renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.getElementById('container').appendChild(renderer.domElement)
light1 = new THREE.DirectionalLight(0xffffffff, 1)
light2 = new THREE.DirectionalLight(0xffffffff, 0.3)
light3 = new THREE.DirectionalLight(0xffffffff, 1)
light1.position.set(1000, -10000, 750)
light2.position.set(-1000, -1000, -750)
light3.position.set(1000, 1000, 750)
scene.add(light1)
scene.add(light2)
scene.add(light3)
atmospere = new THREE.Mesh \
new THREE.SphereGeometry(20000, 60, 49),
new THREE.MeshBasicMaterial(color: 0x555555)
atmospere.scale.x = -1
scene.add atmospere
camera = new THREE.PerspectiveCamera \
70,
window.innerWidth / window.innerHeight,
1,
40 * 1000
camera.position.set(-3000, 3000, -3000)
scene.add(camera)
targetTile = window.tileKaruizawa
boundary = tileToBoundary(targetTile.x, targetTile.y, targetTile.z)
center = centroid(boundary)
project = createProjection(center)
loading = $.when [
loadOverpassData(boundary)
]...
loading.then (overpassData) ->
geoObject = createGeoObject(project, overpassData)
scene.add(geoObject)
controls = new THREE.TrackballControls(camera, renderer.domElement)
controls.target = geoObject.position.clone()
controls.target.add(new THREE.Vector3(0, 0, 0))
requestAnimationFrame animate = ->
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment