Skip to content

Instantly share code, notes, and snippets.

@brunob
Forked from DGuidi/TileLayer.TileJSON.js
Created April 24, 2012 13:10
Show Gist options
  • Save brunob/2479540 to your computer and use it in GitHub Desktop.
Save brunob/2479540 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="http://cdn.leafletjs.com/leaflet-0.4.4/leaflet.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.4.4/leaflet.ie.css" />
<![endif]-->
</head>
<body>
<div id="map" style="width: 800px; height: 600px">
</div>
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<script src="http://cdn.leafletjs.com/leaflet-0.4.4/leaflet.js"></script>
<script src="TileLayer.TileJSON.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var lon = -4.5;
var lat = 48;
var zoom = 4;
var map = new L.Map('map');
var base = new L.TileLayer('http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-light/{z}/{x}/{y}.png', { maxZoom: 17 });
map.addLayer(base);
var tile = new L.TileLayer.TileJSON({debug: false});
tile.createUrl = function (bounds,ctx) {
var url = 'http://tiles.kupaia.fr/tiles.py/geodiv/'+ctx.zoom+'/'+ctx.tile.x+'/'+ctx.tile.y+'.geojson';
return url;
};
tile.styleFor = function (feature) {
var type = feature.geometry.type;
switch (type) {
case 'Point':
case 'MultiPoint':
return {
color: 'rgba(0,192,248,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: this._getZoomForUrl() // fix for https://github.com/CloudMade/Leaflet/pull/993
};
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, properties) {
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,ctx);
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, feature.properties);
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,ctx) {
// 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
}
});
@brunob
Copy link
Author

brunob commented Apr 24, 2012

  1. Updated in order to allow use of tilestache geosjon layers like that :
    var tile = new L.TileLayer.TileJSON();
    tile.createUrl = function (bounds,ctx) {
        var url = 'http://92.243.26.168/tiles/tiles.py/geodiv/'+ctx.zoom+'/'+ctx.tile.x+'/'+ctx.tile.y+'.geojson';
        return url;
    };
  1. Pass feature.properties to drawpoint() internal function (allowing to display markers with personnal icons for each one)

@brunob
Copy link
Author

brunob commented Sep 24, 2012

c2c3b6 make demo works everywhere by using Leaflet cdn scripts and tiled geosjon available onlin

599db4 update demo to use latest Leaflet 0.0.4 + fix for https://github.com/CloudMade/Leaflet/pull/993

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment