Skip to content

Instantly share code, notes, and snippets.

@netconstructor
Forked from DGuidi/TileLayer.TileJSON.js
Created May 1, 2012 08:59
Show Gist options
  • Save netconstructor/2566567 to your computer and use it in GitHub Desktop.
Save netconstructor/2566567 to your computer and use it in GitHub Desktop.
Leaflet TileCanvas
<!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,
point: {
color: 'rgba(252,146,114,0.6)',
radius: 5
},
linestring: {
color: 'rgba(161,217,155,0.8)',
size: 3
},
polygon: {
color: 'rgba(43,140,190,0.4)',
outline: {
color: 'rgb(0,0,0)',
size: 1
}
}
});
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>
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));
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