The most common free alternative is, without a doubt, OpenLayers. It provides an incredibly complete API (much more than Google and Bing) and integration with varied data sources (see examples here). The problem with OpenLayers, in my opinion, is it complexity. I'm also not particularly fond of the map control itself, but that's probably just personal taste.
Anyway, recently appeared a new kid on the block: Leaflet. It is a free lightweight mapping solution that provides a rich and extensible API, very much focused on HTML5. Also, it includes something that a lot of developers are asking for in Bing Maps V7: a client-side canvas layer. In this post I'm going to show how it works and develop a working prototype.
Update (21/01/2013): I've added a working example here
The example will be very similar to the one in my post Bing Maps and HTML5 (Part 1). The main difference will be that the points are going to be created using HTML5 Canvas instead of pushpins. I've already done that in another of my posts, so what's really the difference here? The difference is that, instead of drawing a canvas above the map, we're painting a tile-layer manually. That might seem confusing so let me try to rephrase it:
Leaflet allows the composition of several layers. In this case there will be 2: the base map and the 18 districts of Portugal, represented as numbered circles.
The base map shows the roads, cities, forests, etc, and is based on OpenStreetMap (I'll be blogging soon about OSM).
This base map, like Google Maps and Bing Maps, is tile-based. Meaning that a map view is not composed of a single image but from a set of tiles, placed in a grid-like structure. Each tile has a different image and new tiles are loaded when moving/zooming the map.
It is very common to generate tiles on the server which are fetched as images and displayed as overlays in the map. Leaflet provides a new alternative: tiles generated at client side. The behavior is very similar to the ones at server-side, but a callback is provided to the developer so that he may "paint" the tile using HTML5 Canvas.
This is the skeleton of the implementation:
map = new L.Map('map'); map.setView(new L.LatLng(39.5, -8.5), 6); var tiles = new L.TileLayer.Canvas(); tiles.drawTile = function (canvas, tile, zoom) { var context = canvas.getContext('2d'); //paint tile } map.addLayer(tiles);Obviously it will not be that simple, as there is some math to convert WGS84 coordinates to the pixel coordinates of the tile. Anyway, the full implementation for the circles that appear in the image is:
var tiles = new L.TileLayer.Canvas(); tiles.drawTile = function (canvas, tile, zoom) { var context = canvas.getContext('2d'); // circle radius var radius = 12; var tileSize = this.options.tileSize; for (var i = 0; i < points.length; i++) { var point = new L.LatLng(points[i].lat, points[i].lon); // start coordinates to tile pixels var start = tile.multiplyBy(tileSize); // actual coordinates to tile pixel var p = map.project(point); // point to draw var x = Math.round(p.x - start.x); var y = Math.round(p.y - start.y); // Circle context.beginPath(); context.arc(x, y, radius, 0, 2 * Math.PI, false); // Fill (Gradient) var grd = context.createRadialGradient(x, y, 5, x, y, radius); grd.addColorStop(0, "#8ED6FF"); grd.addColorStop(1, "#004CB3"); context.fillStyle = grd; // Shadow context.shadowColor = "#666666"; context.shadowBlur = 5; context.shadowOffsetX = 7; context.shadowOffsetY = 7; context.fill() context.lineWidth = 2; context.strokeStyle = "black"; context.stroke(); // Text context.lineWidth = 1; context.fillStyle = "#000000"; context.lineStyle = "#000000"; context.font = "12px sans-serif"; context.textAlign = "center"; context.textBaseline = "middle"; context.fillText(i + 1, x, y); } }Also, and like with my previous post, I've included a video showing the usage of this map.
Update (21/01/2013): I've added a working example here
This comment has been removed by the author.
ReplyDeleteHi Pedro,
ReplyDeleteinteresting sample. But one question: if you print the map, the blue circles printed also?
Thanks.
Marcus
Where are you getting tile.multiplyBy() ?
ReplyDeleteTypeError: Object Point(2, 1) has no method 'multipleBy'
Is the base layer (OSM) generated on the client or server side?
ReplyDeleteFrom what you wrote in the post I understood you generate it on the client side using canvas, but the code only references the circles and not the base layer.
Thanks
Gil
Hi Gil,
ReplyDeleteThe base layer is an OSM tile layer (server-side). The code is incomplete and just shows the canvas tile layer. I'll try to include a running example of this example on the post.
I've just added a working example on the post. You can check it at: http://psousa.net/demos/html5leaflet/
ReplyDeleteHey Pedro, this is pretty cool. What advantage does this have over using individual markers (push pins) on the map? Is it faster for the client side or anything like that?
ReplyDeleteFor this example in particular the main advantage is the ability to create dynamic markers (although one could generate them dynamically in server-side). But where I'm showing simple markers there could be complex polygons/polygons, which provides some additional possibilities
ReplyDeleteThanks, just what I was looking for
ReplyDeleteHi Pedro, thank you for your answer.I try to create polygon but it is very slow to show (I have about 100000 polygon in the map)is it possible to show me how to implement polygon.
ReplyDelete