Created
February 1, 2012 08:49
-
-
Save DGuidi/1716010 to your computer and use it in GitHub Desktop.
Leaflet TileCanvas
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Leaflet TileJSON</title> | |
<meta charset="utf-8" /> | |
<link rel="stylesheet" href="Content/leaflet.css" /> | |
<!--[if lte IE 8]><link rel="stylesheet" href="Content/leaflet.ie.css" /><![endif]--> | |
</head> | |
<body> | |
<div id="map" style="width: 800px; height: 600px"> | |
</div> | |
<script src="Scripts/jquery-1.7.1.min.js"></script> | |
<script src="Scripts/leaflet.js"></script> | |
<script src="TileLayer.TileJSON.js"></script> | |
<script type="text/javascript"> | |
$(document).ready(function () { | |
var lon = -73.9529; | |
var lat = 40.7723; | |
var zoom = 10; | |
var map = new L.Map('map'); | |
var cloudmade = new L.TileLayer('http://{s}.tile.cloudmade.com/1a1b06b230af4efdbb989ea99e9841af/997/256/{z}/{x}/{y}.png', { maxZoom: 18 }); | |
map.addLayer(cloudmade); | |
var tile = new L.TileLayer.TileJSON({ | |
debug: false, | |
// this value should be equal to 'radius' of your points | |
buffer: 5 | |
}); | |
tile.createUrl = function (bounds) { | |
var url = ['/json.ashx?MAP_TYPE=PM&BBOX=', | |
bounds[0], ',', | |
bounds[1], ',', | |
bounds[2], ',', | |
bounds[3] | |
].join(''); | |
return url; | |
}; | |
tile.styleFor = function (feature) { | |
var type = feature.geometry.type; | |
switch (type) { | |
case 'Point': | |
case 'MultiPoint': | |
return { | |
color: 'rgba(252,146,114,0.6)', | |
radius: 5 | |
}; | |
case 'LineString': | |
case 'MultiLineString': | |
return { | |
color: 'rgba(161,217,155,0.8)', | |
size: 3 | |
}; | |
case 'Polygon': | |
case 'MultiPolygon': | |
return { | |
color: 'rgba(43,140,190,0.4)', | |
outline: { | |
color: 'rgb(0,0,0)', | |
size: 1 | |
} | |
}; | |
default: | |
return null; | |
} | |
}; | |
map.addLayer(tile); | |
var center = new L.LatLng(lat, lon); | |
map.setView(center, zoom); | |
}); | |
</script> | |
</body> | |
</html> |
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
L.TileLayer.TileJSON = L.TileLayer.Canvas.extend({ | |
options: { | |
debug: false | |
}, | |
tileSize: 256, | |
initialize: function (options) { | |
L.Util.setOptions(this, options); | |
this.drawTile = function (canvas, tilePoint, zoom) { | |
var ctx = { | |
canvas: canvas, | |
tile: tilePoint, | |
zoom: zoom | |
}; | |
if (this.options.debug) { | |
this._drawDebugInfo(ctx); | |
} | |
this._draw(ctx); | |
}; | |
}, | |
_drawDebugInfo: function (ctx) { | |
var max = this.tileSize; | |
var g = ctx.canvas.getContext('2d'); | |
g.strokeStyle = '#000000'; | |
g.fillStyle = '#FFFF00'; | |
g.strokeRect(0, 0, max, max); | |
g.font = "12px Arial"; | |
g.fillRect(0, 0, 5, 5); | |
g.fillRect(0, max - 5, 5, 5); | |
g.fillRect(max - 5, 0, 5, 5); | |
g.fillRect(max - 5, max - 5, 5, 5); | |
g.fillRect(max / 2 - 5, max / 2 - 5, 10, 10); | |
g.strokeText(ctx.tile.x + ' ' + ctx.tile.y + ' ' + ctx.zoom, max / 2 - 30, max / 2 - 10); | |
}, | |
_tilePoint: function (ctx, coords) { | |
// start coords to tile 'space' | |
var s = ctx.tile.multiplyBy(this.tileSize); | |
// actual coords to tile 'space' | |
var p = this._map.project(new L.LatLng(coords[1], coords[0])); | |
// point to draw | |
var x = Math.round(p.x - s.x); | |
var y = Math.round(p.y - s.y); | |
return { | |
x: x, | |
y: y | |
}; | |
}, | |
_clip: function (ctx, points) { | |
var nw = ctx.tile.multiplyBy(this.tileSize); | |
var se = nw.add(new L.Point(this.tileSize, this.tileSize)); | |
var bounds = new L.Bounds([nw, se]); | |
var len = points.length; | |
var out = []; | |
for (var i = 0; i < len - 1; i++) { | |
var seg = L.LineUtil.clipSegment(points[i], points[i + 1], bounds, i); | |
if (!seg) { | |
continue; | |
} | |
out.push(seg[0]); | |
// if segment goes out of screen, or it's the last one, it's the end of the line part | |
if ((seg[1] !== points[i + 1]) || (i === len - 2)) { | |
out.push(seg[1]); | |
} | |
} | |
return out; | |
}, | |
_isActuallyVisible: function (coords) { | |
var coord = coords[0]; | |
var min = [coord.x, coord.y], max = [coord.x, coord.y]; | |
for (var i = 1; i < coords.length; i++) { | |
coord = coords[i]; | |
min[0] = Math.min(min[0], coord.x); | |
min[1] = Math.min(min[1], coord.y); | |
max[0] = Math.max(max[0], coord.x); | |
max[1] = Math.max(max[1], coord.y); | |
} | |
var diff0 = max[0] - min[0]; | |
var diff1 = max[1] - min[1]; | |
if (this.options.debug) { | |
console.log(diff0 + ' ' + diff1); | |
} | |
var visible = diff0 > 1 || diff1 > 1; | |
return visible; | |
}, | |
_drawPoint: function (ctx, geom, style) { | |
if (!style) { | |
return; | |
} | |
var p = this._tilePoint(ctx, geom); | |
var c = ctx.canvas; | |
var g = c.getContext('2d'); | |
g.beginPath(); | |
g.fillStyle = style.color; | |
g.arc(p.x, p.y, style.radius, 0, Math.PI * 2); | |
g.closePath(); | |
g.fill(); | |
g.restore(); | |
}, | |
_drawLineString: function (ctx, geom, style) { | |
if (!style) { | |
return; | |
} | |
var coords = geom, proj = [], i; | |
coords = this._clip(ctx, coords); | |
coords = L.LineUtil.simplify(coords, 1); | |
for (i = 0; i < coords.length; i++) { | |
proj.push(this._tilePoint(ctx, coords[i])); | |
} | |
if (!this._isActuallyVisible(proj)) { | |
return; | |
} | |
var g = ctx.canvas.getContext('2d'); | |
g.strokeStyle = style.color; | |
g.lineWidth = style.size; | |
g.beginPath(); | |
for (i = 0; i < proj.length; i++) { | |
var method = (i === 0 ? 'move' : 'line') + 'To'; | |
g[method](proj[i].x, proj[i].y); | |
} | |
g.stroke(); | |
g.restore(); | |
}, | |
_drawPolygon: function (ctx, geom, style) { | |
if (!style) { | |
return; | |
} | |
for (var el = 0; el < geom.length; el++) { | |
var coords = geom[el], proj = [], i; | |
coords = this._clip(ctx, coords); | |
for (i = 0; i < coords.length; i++) { | |
proj.push(this._tilePoint(ctx, coords[i])); | |
} | |
if (!this._isActuallyVisible(proj)) { | |
continue; | |
} | |
var g = ctx.canvas.getContext('2d'); | |
var outline = style.outline; | |
g.fillStyle = style.color; | |
if (outline) { | |
g.strokeStyle = outline.color; | |
g.lineWidth = outline.size; | |
} | |
g.beginPath(); | |
for (i = 0; i < proj.length; i++) { | |
var method = (i === 0 ? 'move' : 'line') + 'To'; | |
g[method](proj[i].x, proj[i].y); | |
} | |
g.closePath(); | |
g.fill(); | |
if (outline) { | |
g.stroke(); | |
} | |
} | |
}, | |
_draw: function (ctx) { | |
// NOTE: this is the only part of the code that depends from external libraries (actually, jQuery only). | |
var loader = $.getJSON; | |
var nwPoint = ctx.tile.multiplyBy(this.tileSize); | |
var sePoint = nwPoint.add(new L.Point(this.tileSize, this.tileSize)); | |
// optionally, enlarge request area. | |
// with this I can draw points with coords outside this tile area, | |
// but with part of the graphics actually inside this tile. | |
// NOTE: that you should use this option only if you're actually drawing points! | |
var buf = this.options.buffer; | |
if (buf > 0) { | |
var diff = new L.Point(buf, buf); | |
nwPoint = nwPoint.subtract(diff); | |
sePoint = sePoint.add(diff); | |
} | |
var nwCoord = this._map.unproject(nwPoint, ctx.zoom, true); | |
var seCoord = this._map.unproject(sePoint, ctx.zoom, true); | |
var bounds = [nwCoord.lng, seCoord.lat, seCoord.lng, nwCoord.lat]; | |
var url = this.createUrl(bounds); | |
var self = this, j; | |
loader(url, function (data) { | |
for (var i = 0; i < data.features.length; i++) { | |
var feature = data.features[i]; | |
var style = self.styleFor(feature); | |
var type = feature.geometry.type; | |
var geom = feature.geometry.coordinates; | |
var len = geom.length; | |
switch (type) { | |
case 'Point': | |
self._drawPoint(ctx, geom, style); | |
break; | |
case 'MultiPoint': | |
for (j = 0; j < len; j++) { | |
self._drawPoint(ctx, geom[j], style); | |
} | |
break; | |
case 'LineString': | |
self._drawLineString(ctx, geom, style); | |
break; | |
case 'MultiLineString': | |
for (j = 0; j < len; j++) { | |
self._drawLineString(ctx, geom[j], style); | |
} | |
break; | |
case 'Polygon': | |
self._drawPolygon(ctx, geom, style); | |
break; | |
case 'MultiPolygon': | |
for (j = 0; j < len; j++) { | |
self._drawPolygon(ctx, geom[j], style); | |
} | |
break; | |
default: | |
throw new Error('Unmanaged type: ' + type); | |
} | |
} | |
}); | |
}, | |
// NOTE: a placeholder for a function that, given a tile context, returns a string to a GeoJSON service that retrieve features for that context | |
createUrl: function (bounds) { | |
// override with your code | |
}, | |
// NOTE: a placeholder for a function that, given a feature, returns a style object used to render the feature itself | |
styleFor: function (feature) { | |
// override with your code | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hi, thanks for this plugin, its great.
One of the problems is that at exactly each zoom in/out it fires the AJAX request, so if someone zooms and pan very fast, many old requests are still hitting the database.
I need to find a way to apply a delay to the _draw function AND abort these 'old' AJAX request
This last option, I am trying to hack as follows using _debounce (http://lodash.com/docs#debounce)
but it fires absolutely nothing, not even the alert and I get no info in console.
Any ideas? thank you!