Last active
August 29, 2015 13:57
-
-
Save i09158knct/9709568 to your computer and use it in GitHub Desktop.
2014-03-23 3rd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body{margin:0;padding:0;overflow:hidden} | |
.banner{background-color:rgba(204,204,204,0.333);position:fixed;top:0;left:0} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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"> | |
© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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