Created
November 29, 2014 20:20
-
-
Save summer4096/677c4a66eea9cf3b8ee9 to your computer and use it in GitHub Desktop.
Hill shading w/ leaflet!
This file contains hidden or 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
L.tileLayer.fancyCanvas = function(url){ | |
var layer = L.tileLayer.canvas({async: true}); | |
layer.setUrl(url); | |
var dataSource = function(x, y, z, done){ | |
var url = layer.getTileUrl({x: x, y: y, z: z}); | |
d3.xhr(url).responseType('arraybuffer').get(done); | |
}; | |
layer.data = function(fn){ | |
dataSource = fn; | |
return layer; | |
}; | |
layer.projections = {}; | |
layer.projections.WGS84 = function(offset){ | |
offset = offset || {x: 0, y: 0}; | |
return d3.geo.transform({ | |
point: function(y, x) { | |
var point = layer._map.latLngToLayerPoint(new L.LatLng(x, y)); | |
this.stream.point(point.x-tileOffset.x, point.y-tileOffset.y); | |
} | |
}); | |
}; | |
layer.modes = {}; | |
layer.modes.geojson = { | |
extensions: ['geojson', 'json'], | |
get: function(url, callback){ | |
d3.json(url, callback); | |
}, | |
parse: function(data, canvas){ | |
var tileOffset = { | |
x: parseInt(d3.select(canvas).style('left').slice(0, -2)), | |
y: parseInt(d3.select(canvas).style('top').slice(0, -2)) | |
}; | |
return { | |
data: {layer: data}, | |
projection: layer.projections.WGS84(tileOffset) | |
} | |
} | |
}; | |
layer.modes.topojson = { | |
extensions: ['topojson'], | |
get: function(url, callback){ | |
d3.json(url, callback); | |
}, | |
parse: function(data, canvas){ | |
var tileOffset = { | |
x: parseInt(d3.select(canvas).style('left').slice(0, -2)), | |
y: parseInt(d3.select(canvas).style('top').slice(0, -2)) | |
}; | |
var layers = {}; | |
for (var key in data.objects) { | |
layers[key] = topojson.feature(data, data.objects[key]); | |
} | |
return { | |
data: layers, | |
projection: layer.projections.WGS84(tileOffset) | |
} | |
} | |
}; | |
layer.modes.protobuf = { | |
extensions: ['mvt', 'pbf'], | |
get: function(url, callback){ | |
d3.xhr(url).responseType('arraybuffer').get(callback); | |
}, | |
parse: function(data, canvas){ | |
var tile = new vectorTile.VectorTile( new pbf( new Uint8Array(data) ) ); | |
var layers = {}; | |
for (var key in tile.layers) { | |
layers[key] = tile.layers[key].toGeoJSON(); | |
} | |
//console.log(layers); | |
return { | |
data: layers, | |
projection: d3.geo.transform({ | |
point: function(x, y) { | |
x = x/tile.layers[layer.__currentLayer].extent*canvas.width*(1/window.devicePixelRatio); | |
y = y/tile.layers[layer.__currentLayer].extent*canvas.height*(1/window.devicePixelRatio); | |
this.stream.point(x, y); | |
} | |
}) | |
}; | |
} | |
}; | |
var modeOption = 'auto'; | |
layer.mode = function(_mode){ | |
modeOption = _mode; | |
return layer; | |
}; | |
var renderers = []; | |
layer.render = function(layerName, fn){ | |
renderers.push({ | |
layer: layerName, | |
run: fn | |
}); | |
return layer; | |
}; | |
layer.drawTile = function(canvas, tilePoint, zoom) { | |
var context = canvas.getContext('2d'); | |
tilePoint = {x: tilePoint.x, y: tilePoint.y, z: zoom}; | |
var mode; | |
if (modeOption == 'auto') { | |
var extension = layer._url.split('?')[0].split('.').pop(); | |
for (var key in layer.modes) { | |
if (layer.modes[key].extensions.indexOf(extension) != -1) { | |
mode = layer.modes[key]; | |
break; | |
} | |
} | |
if (!mode) { | |
throw new Error('I don\'t know what to do with URLs ending in .'+extension); | |
} | |
} else { | |
mode = layer.modes[modeOption]; | |
} | |
var url = layer.getTileUrl(tilePoint); | |
mode.get(url, function(err, xhr){ | |
if (err) { | |
throw err; | |
} | |
var result = mode.parse(xhr.response, canvas); | |
var path = d3.geo.path() | |
.projection(result.projection) | |
.context(context); | |
if (renderers.length) { | |
renderers.forEach(function(renderer){ | |
if (!result.data[renderer.layer]) return; | |
layer.__currentLayer = renderer.layer; | |
result.data[renderer.layer].features.forEach(function(feature){ | |
context.beginPath(); | |
path(feature); | |
renderer.run(context, feature, tilePoint); | |
}); | |
}); | |
} else { | |
throw new Error('No renderer specified!'); | |
} | |
layer.tileDrawn(canvas); | |
}); | |
}; | |
return layer; | |
}; |
This file contains hidden or 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Canvas Layer!</title> | |
<meta charset="utf-8"> | |
<link rel="stylesheet" type="text/css" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css"> | |
<link rel="stylesheet" type="text/css" href="main.css"> | |
</head> | |
<body> | |
<div id="map"></div> | |
<script src="https://cdn.rawgit.com/jondavidjohn/hidpi-canvas-polyfill/master/dist/hidpi-canvas.js"></script> | |
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script> | |
<script src="http://d3js.org/d3.v3.js"></script> | |
<script src="http://d3js.org/topojson.v1.min.js"></script> | |
<script src="http://wzrd.in/standalone/pbf@latest"></script> | |
<script src="vectortile.js"></script> | |
<script src="fancyCanvas.js"></script> | |
<script src="layer.js"></script> | |
</body> | |
</html> |
This file contains hidden or 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
var map = L.map('map', { | |
center: [44.327,-72.888], | |
zoom: 12, | |
zoomControl: false | |
}); | |
//L.tileLayer('http://{s}.tile.stamen.com/terrain-background/{z}/{x}/{y}.jpg').addTo(map); | |
//var url = 'http://{s}.tile.openstreetmap.us/vectiles-highroad/{z}/{x}/{y}.topojson'; | |
var url = 'https://{s}.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6-dev,mapbox.mapbox-terrain-v1/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6IlhHVkZmaW8ifQ.hAMX5hSW-QnTeRCMAy9A8Q'; | |
var roadSizes = { | |
"highway": 5, | |
"major_road": 3, | |
"minor_road": 1, | |
"rail": 0, | |
"path": 0.5 | |
}; | |
var colors = { | |
land: '#FCFBE7', | |
water: '#368ed9', | |
grass: '#E6F2C1', | |
beach: '#FFEEC7', | |
park: '#DAF2C1', | |
cemetery: '#D6DED2', | |
wooded: '#C3D9AD', | |
agriculture: '#F2E8B6', | |
building: '#E4E0E0', | |
hospital: 'rgb(229,198,195)', | |
school: '#FFF5CC', | |
sports: '#B8E6B8', | |
residential: '#FCFBE7', | |
commercial: '#FCFBE7', | |
industrial: '#FCFBE7', | |
parking: '#EEE', | |
big_road: '#d28585', | |
little_road: '#bbb' | |
}; | |
var landColors = { | |
cemetery: colors.cemetery, | |
college: colors.school, | |
commercial: colors.industrial, | |
common: colors.park, | |
forest: colors.wooded, | |
golf_course: colors.sports, | |
grass: colors.grass, | |
hospital: colors.hospital, | |
industrial: colors.industrial, | |
park: colors.park, | |
parking: colors.parking, | |
pedestrian: colors.pedestrian_fill, | |
pitch: colors.sports, | |
residential: colors.residential, | |
school: colors.school, | |
sports_center: colors.sports, | |
stadium: colors.sports, | |
university: colors.school, | |
wood: colors.wooded | |
}; | |
L.tileLayer.fancyCanvas(url) | |
.render('landuse', function(context, d, tile){ | |
if (tile.z > 12 && landColors[d.properties.class]) { | |
context.fillStyle = landColors[d.properties.class]; | |
context.fill(); | |
} | |
}) | |
.render('hillshade', function(context, d){ | |
var parts = d.properties.class.split('_'); | |
var amount = parts[0]; | |
var type = parts[1]; | |
var shadow = '100, 50, 150'; | |
var highlight = '255, 255, 150'; | |
var alpha = 1; | |
if (amount == 'medium') { | |
alpha = 0.2; | |
} else if (amount == 'full') { | |
alpha = 0.3; | |
} | |
context.fillStyle = 'rgba('+(type == 'shadow' ? shadow : highlight)+', '+alpha+')'; | |
context.fill(); | |
}) | |
.render('contour', function(context, d){ | |
context.strokeStyle = 'rgba(0,0,0,0.2)'; | |
context.lineWidth = 0.5; | |
context.stroke(); | |
}) | |
.render('road', function(context, d, tile){ | |
var big = (d.properties.type == 'motorway' || d.properties.type == 'trunk'); | |
context.strokeStyle = big ? colors.big_road : colors.little_road; | |
context.lineWidth = 1; | |
if (tile.z < 8) { | |
if (big) context.stroke(); | |
} else { | |
context.stroke(); | |
} | |
}) | |
.render('building', function(context, d){ | |
context.fillStyle = '#666'; | |
context.fill(); | |
}) | |
.render('water', function(context, d){ | |
context.fillStyle = colors.water; | |
context.fill(); | |
}) | |
.render('waterway', function(context, d){ | |
context.strokeStyle = colors.water; | |
context.lineWidth = 1; | |
context.stroke(); | |
}) | |
.addTo(map); |
This file contains hidden or 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
html, body { | |
height: 100%; | |
padding: 0; | |
margin: 0; | |
} | |
#map { | |
width: 100%; | |
height: 100%; | |
background: #bcd9bf; | |
} |
This file contains hidden or 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
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.vectorTile=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
'use strict'; | |
var VectorTileLayer = require('./vectortilelayer'); | |
module.exports = VectorTile; | |
function VectorTile(buffer, end) { | |
this.layers = {}; | |
this._buffer = buffer; | |
end = end || buffer.length; | |
while (buffer.pos < end) { | |
var val = buffer.readVarint(), | |
tag = val >> 3; | |
if (tag == 3) { | |
var layer = this.readLayer(); | |
if (layer.length) this.layers[layer.name] = layer; | |
} else { | |
buffer.skip(val); | |
} | |
} | |
} | |
VectorTile.prototype.readLayer = function() { | |
var buffer = this._buffer, | |
bytes = buffer.readVarint(), | |
end = buffer.pos + bytes, | |
layer = new VectorTileLayer(buffer, end); | |
buffer.pos = end; | |
return layer; | |
}; | |
// Returns a dictionary of layers as individual GeoJSON feature collections, keyed by layer name | |
VectorTile.prototype.toGeoJSON = function () { | |
var json = {}; | |
var layerNames = Object.keys(this.layers); | |
for (var n=0; n < layerNames.length; n++) { | |
json[layerNames[n]] = this.layers[layerNames[n]].toGeoJSON(); | |
} | |
return json; | |
}; | |
},{"./vectortilelayer":3}],2:[function(require,module,exports){ | |
'use strict'; | |
var Point = require('point-geometry'); | |
module.exports = VectorTileFeature; | |
function VectorTileFeature(buffer, end, extent, keys, values) { | |
this.properties = {}; | |
// Public | |
this.extent = extent; | |
this.type = 0; | |
// Private | |
this._buffer = buffer; | |
this._geometry = -1; | |
this._keys = keys; | |
end = end || buffer.length; | |
while (buffer.pos < end) { | |
var val = buffer.readVarint(), | |
tag = val >> 3; | |
if (tag == 1) { | |
this._id = buffer.readVarint(); | |
} else if (tag == 2) { | |
var tagEnd = buffer.pos + buffer.readVarint(); | |
while (buffer.pos < tagEnd) { | |
var key = keys[buffer.readVarint()]; | |
var value = values[buffer.readVarint()]; | |
this.properties[key] = value; | |
} | |
} else if (tag == 3) { | |
this.type = buffer.readVarint(); | |
} else if (tag == 4) { | |
this._geometry = buffer.pos; | |
buffer.skip(val); | |
} else { | |
buffer.skip(val); | |
} | |
} | |
} | |
VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon']; | |
VectorTileFeature.prototype.loadGeometry = function() { | |
var buffer = this._buffer; | |
buffer.pos = this._geometry; | |
var bytes = buffer.readVarint(), | |
end = buffer.pos + bytes, | |
cmd = 1, | |
length = 0, | |
x = 0, | |
y = 0, | |
lines = [], | |
line; | |
while (buffer.pos < end) { | |
if (!length) { | |
var cmd_length = buffer.readVarint(); | |
cmd = cmd_length & 0x7; | |
length = cmd_length >> 3; | |
} | |
length--; | |
if (cmd === 1 || cmd === 2) { | |
x += buffer.readSVarint(); | |
y += buffer.readSVarint(); | |
if (cmd === 1) { | |
// moveTo | |
if (line) { | |
lines.push(line); | |
} | |
line = []; | |
} | |
line.push(new Point(x, y)); | |
} else if (cmd === 7) { | |
// closePolygon | |
line.push(line[0].clone()); | |
} else { | |
throw new Error('unknown command ' + cmd); | |
} | |
} | |
if (line) lines.push(line); | |
return lines; | |
}; | |
VectorTileFeature.prototype.bbox = function() { | |
var buffer = this._buffer; | |
buffer.pos = this._geometry; | |
var bytes = buffer.readVarint(), | |
end = buffer.pos + bytes, | |
cmd = 1, | |
length = 0, | |
x = 0, | |
y = 0, | |
x1 = Infinity, | |
x2 = -Infinity, | |
y1 = Infinity, | |
y2 = -Infinity; | |
while (buffer.pos < end) { | |
if (!length) { | |
var cmd_length = buffer.readVarint(); | |
cmd = cmd_length & 0x7; | |
length = cmd_length >> 3; | |
} | |
length--; | |
if (cmd === 1 || cmd === 2) { | |
x += buffer.readSVarint(); | |
y += buffer.readSVarint(); | |
if (x < x1) x1 = x; | |
if (x > x2) x2 = x; | |
if (y < y1) y1 = y; | |
if (y > y2) y2 = y; | |
} else if (cmd !== 7) { | |
throw new Error('unknown command ' + cmd); | |
} | |
} | |
return [x1, y1, x2, y2]; | |
}; | |
VectorTileFeature.prototype.toGeoJSON = function () { | |
var geojson = { | |
type: 'Feature', | |
geometry: {}, | |
properties: {} | |
}; | |
for (var k=0; k < this._keys.length; k++) { | |
var key = this._keys[k]; | |
geojson.properties[key] = this.properties[key]; | |
} | |
geojson.geometry.coordinates = this.loadGeometry(); | |
for (var r=0; r < geojson.geometry.coordinates.length; r++) { | |
var ring = geojson.geometry.coordinates[r]; | |
for (var c=0; c < ring.length; c++) { | |
ring[c] = [ | |
ring[c].x, | |
ring[c].y | |
]; | |
} | |
} | |
if (VectorTileFeature.types[this.type] == 'Point') { | |
geojson.geometry.type = 'Point'; | |
} | |
else if (VectorTileFeature.types[this.type] == 'LineString') { | |
if (geojson.geometry.coordinates.length == 1) { | |
geojson.geometry.coordinates = geojson.geometry.coordinates[0]; | |
geojson.geometry.type = 'LineString'; | |
} | |
else { | |
geojson.geometry.type = 'MultiLineString'; | |
} | |
} | |
else if (VectorTileFeature.types[this.type] == 'Polygon') { | |
geojson.geometry.type = 'Polygon'; | |
} | |
return geojson; | |
}; | |
},{"point-geometry":4}],3:[function(require,module,exports){ | |
'use strict'; | |
var VectorTileFeature = require('./vectortilefeature.js'); | |
module.exports = VectorTileLayer; | |
function VectorTileLayer(buffer, end) { | |
// Public | |
this.version = 1; | |
this.name = null; | |
this.extent = 4096; | |
this.length = 0; | |
// Private | |
this._buffer = buffer; | |
this._keys = []; | |
this._values = []; | |
this._features = []; | |
var val, tag; | |
end = end || buffer.length; | |
while (buffer.pos < end) { | |
val = buffer.readVarint(); | |
tag = val >> 3; | |
if (tag === 15) { | |
this.version = buffer.readVarint(); | |
} else if (tag === 1) { | |
this.name = buffer.readString(); | |
} else if (tag === 5) { | |
this.extent = buffer.readVarint(); | |
} else if (tag === 2) { | |
this.length++; | |
this._features.push(buffer.pos); | |
buffer.skip(val); | |
} else if (tag === 3) { | |
this._keys.push(buffer.readString()); | |
} else if (tag === 4) { | |
this._values.push(this.readFeatureValue()); | |
} else { | |
buffer.skip(val); | |
} | |
} | |
} | |
VectorTileLayer.prototype.readFeatureValue = function() { | |
var buffer = this._buffer, | |
value = null, | |
bytes = buffer.readVarint(), | |
end = buffer.pos + bytes, | |
val, tag; | |
while (buffer.pos < end) { | |
val = buffer.readVarint(); | |
tag = val >> 3; | |
if (tag == 1) { | |
value = buffer.readString(); | |
} else if (tag == 2) { | |
throw new Error('read float'); | |
} else if (tag == 3) { | |
value = buffer.readDouble(); | |
} else if (tag == 4) { | |
value = buffer.readVarint(); | |
} else if (tag == 5) { | |
throw new Error('read uint'); | |
} else if (tag == 6) { | |
value = buffer.readSVarint(); | |
} else if (tag == 7) { | |
value = Boolean(buffer.readVarint()); | |
} else { | |
buffer.skip(val); | |
} | |
} | |
return value; | |
}; | |
// return feature `i` from this layer as a `VectorTileFeature` | |
VectorTileLayer.prototype.feature = function(i) { | |
if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds'); | |
this._buffer.pos = this._features[i]; | |
var end = this._buffer.readVarint() + this._buffer.pos; | |
return new VectorTileFeature(this._buffer, end, this.extent, this._keys, this._values); | |
}; | |
VectorTileLayer.prototype.toGeoJSON = function () { | |
var geojson = { | |
type: 'FeatureCollection', | |
features: [] | |
}; | |
for (var f=0; f < this.length; f++) { | |
geojson.features.push(this.feature(f).toGeoJSON()); | |
} | |
return geojson; | |
}; | |
},{"./vectortilefeature.js":2}],4:[function(require,module,exports){ | |
'use strict'; | |
module.exports = Point; | |
function Point(x, y) { | |
this.x = x; | |
this.y = y; | |
} | |
Point.prototype = { | |
clone: function() { return new Point(this.x, this.y); }, | |
add: function(p) { return this.clone()._add(p); }, | |
sub: function(p) { return this.clone()._sub(p); }, | |
mult: function(k) { return this.clone()._mult(k); }, | |
div: function(k) { return this.clone()._div(k); }, | |
rotate: function(a) { return this.clone()._rotate(a); }, | |
matMult: function(m) { return this.clone()._matMult(m); }, | |
unit: function() { return this.clone()._unit(); }, | |
perp: function() { return this.clone()._perp(); }, | |
round: function() { return this.clone()._round(); }, | |
mag: function() { | |
return Math.sqrt(this.x * this.x + this.y * this.y); | |
}, | |
equals: function(p) { | |
return this.x === p.x && | |
this.y === p.y; | |
}, | |
dist: function(p) { | |
return Math.sqrt(this.distSqr(p)); | |
}, | |
distSqr: function(p) { | |
var dx = p.x - this.x, | |
dy = p.y - this.y; | |
return dx * dx + dy * dy; | |
}, | |
angle: function() { | |
return Math.atan2(this.y, this.x); | |
}, | |
angleTo: function(b) { | |
return Math.atan2(this.y - b.y, this.x - b.x); | |
}, | |
angleWith: function(b) { | |
return this.angleWithSep(b.x, b.y); | |
}, | |
// Find the angle of the two vectors, solving the formula for the cross product a x b = |a||b|sin(θ) for θ. | |
angleWithSep: function(x, y) { | |
return Math.atan2( | |
this.x * y - this.y * x, | |
this.x * x + this.y * y); | |
}, | |
_matMult: function(m) { | |
var x = m[0] * this.x + m[1] * this.y, | |
y = m[2] * this.x + m[3] * this.y; | |
this.x = x; | |
this.y = y; | |
return this; | |
}, | |
_add: function(p) { | |
this.x += p.x; | |
this.y += p.y; | |
return this; | |
}, | |
_sub: function(p) { | |
this.x -= p.x; | |
this.y -= p.y; | |
return this; | |
}, | |
_mult: function(k) { | |
this.x *= k; | |
this.y *= k; | |
return this; | |
}, | |
_div: function(k) { | |
this.x /= k; | |
this.y /= k; | |
return this; | |
}, | |
_unit: function() { | |
this._div(this.mag()); | |
return this; | |
}, | |
_perp: function() { | |
var y = this.y; | |
this.y = this.x; | |
this.x = -y; | |
return this; | |
}, | |
_rotate: function(angle) { | |
var cos = Math.cos(angle), | |
sin = Math.sin(angle), | |
x = cos * this.x - sin * this.y, | |
y = sin * this.x + cos * this.y; | |
this.x = x; | |
this.y = y; | |
return this; | |
}, | |
_round: function() { | |
this.x = Math.round(this.x); | |
this.y = Math.round(this.y); | |
return this; | |
} | |
}; | |
// constructs Point from an array if necessary | |
Point.convert = function (a) { | |
if (a instanceof Point) { | |
return a; | |
} | |
if (Array.isArray(a)) { | |
return new Point(a[0], a[1]); | |
} | |
return a; | |
}; | |
},{}],5:[function(require,module,exports){ | |
module.exports.VectorTile = require('./lib/vectortile.js'); | |
module.exports.VectorTileFeature = require('./lib/vectortilefeature.js'); | |
module.exports.VectorTileLayer = require('./lib/vectortilelayer.js'); | |
},{"./lib/vectortile.js":1,"./lib/vectortilefeature.js":2,"./lib/vectortilelayer.js":3}]},{},[5])(5) | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment