Simple earcut 1.4 test of triangulation for polygons for webgl drawing. Viedo of 250MB large geojson file:
see discussion also here
<!doctype html> | |
<html> | |
<head> | |
<title>Many polygons with leaflet, earcut and WebGL</title> | |
<meta charset="utf-8"> | |
<style> | |
html, body { | |
height: 100%; | |
padding: 0; | |
margin: 0; | |
background: rgb(14, 21, 30); | |
height: 100%; | |
} | |
#map { | |
position: absolute; | |
height: 100%; | |
width: 100%; | |
background-color: #333; | |
} | |
</style> | |
<!-- vertex shader --> | |
<script id="vshader" type="x-shader/x-vertex"> | |
uniform mat4 u_matrix; | |
attribute vec4 a_vertex; | |
attribute float a_pointSize; | |
attribute vec4 a_color; | |
varying vec4 v_color; | |
void main() { | |
// Set the size of the point | |
gl_PointSize = a_pointSize; | |
// multiply each vertex by a matrix. | |
gl_Position = u_matrix * a_vertex; | |
// pass the color to the fragment shader | |
v_color = a_color; | |
} | |
</script> | |
<!-- fragment shader --> | |
<script id="fshader" type="x-shader/x-fragment"> | |
precision mediump float; | |
varying vec4 v_color; | |
void main() { | |
// -- squares | |
gl_FragColor = v_color; | |
gl_FragColor.a = 0.8; | |
// gl_FragColor =vec4(0.8, 0.1,0.1, 0.9); // v_color; | |
} | |
</script> | |
</head> | |
<body> | |
<div id="map"></div> | |
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" /> | |
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script> | |
<script src="http://www.sumbera.com/gist/js/leaflet/canvas/L.CanvasOverlay.js"></script> | |
<script> | |
// -- module, require mockups so we can use orig files unmodified | |
module = {}; | |
</script> | |
<script src="http://www.sumbera.com/gist/js/earcut/earcut-1.4.2.js"></script> | |
<script src="http://www.sumbera.com/gist/data/CZDistricts.js"></script> | |
<!-- <script src="../data/obceS.js"></script> --> | |
<script> | |
var leafletMap = L.map('map').setView([50.00, 14.44], 8); | |
L.tileLayer("http://{s}.sm.mapstack.stamen.com/(toner-background,$fff[difference],$fff[@23],$fff[hsl-saturation@20],toner-lines[destination-in])/{z}/{x}/{y}.png") | |
.addTo(leafletMap); | |
var glLayer = L.canvasOverlay() | |
.drawing(drawingOnCanvas) | |
.addTo(leafletMap); | |
var canvas = glLayer.canvas(); | |
glLayer.canvas.width = canvas.clientWidth; | |
glLayer.canvas.height = canvas.clientHeight; | |
var gl = canvas.getContext('experimental-webgl', { antialias: true }); | |
var pixelsToWebGLMatrix = new Float32Array(16); | |
var mapMatrix = new Float32Array(16); | |
// -- WebGl setup | |
var vertexShader = gl.createShader(gl.VERTEX_SHADER); | |
gl.shaderSource(vertexShader, document.getElementById('vshader').text); | |
gl.compileShader(vertexShader); | |
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); | |
gl.shaderSource(fragmentShader, document.getElementById('fshader').text); | |
gl.compileShader(fragmentShader); | |
// link shaders to create our program | |
var program = gl.createProgram(); | |
gl.attachShader(program, vertexShader); | |
gl.attachShader(program, fragmentShader); | |
gl.linkProgram(program); | |
gl.useProgram(program); | |
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); | |
gl.enable(gl.BLEND); | |
// gl.disable(gl.DEPTH_TEST); | |
// ---------------------------- | |
// look up the locations for the inputs to our shaders. | |
var u_matLoc = gl.getUniformLocation(program, "u_matrix"); | |
gl.aPointSize = gl.getAttribLocation(program, "a_pointSize"); | |
// Set the matrix to some that makes 1 unit 1 pixel. | |
pixelsToWebGLMatrix.set([2 / canvas.width, 0, 0, 0, 0, -2 / canvas.height, 0, 0, 0, 0, 0, 0, -1, 1, 0, 1]); | |
gl.viewport(0, 0, canvas.width, canvas.height); | |
gl.uniformMatrix4fv(u_matLoc, false, pixelsToWebGLMatrix); | |
// -- data | |
var verts = []; | |
var rawVerts = []; | |
//-- verts only | |
var start = new Date(); | |
for (var f = 0; f < data.features.length ; f++) { | |
rawVerts = []; | |
var feature = data.features[f]; | |
//*** | |
for (var i = 0; i < feature.geometry.coordinates[0].length; i++) { | |
rawVerts.push([feature.geometry.coordinates[0][i][1], feature.geometry.coordinates[0][i][0]]); | |
} | |
rawVerts.pop(); | |
currentColor = [Math.random(), Math.random(), Math.random()]; //[0.1, 0.6, 0.1]; | |
var triangles = earcut([rawVerts]); | |
for (var i = 0; i < triangles.length; i++) { | |
pixel = LatLongToPixelXY(triangles[i][0], triangles[i][1]); | |
verts.push(pixel.x, pixel.y, currentColor[0], currentColor[1], currentColor[2]); | |
} | |
} | |
console.log("updated at " + new Date().setTime(new Date().getTime() - start.getTime()) + " ms "); | |
// tirangles or point count | |
var numPoints = verts.length / 5; | |
console.log("num points: " + numPoints); | |
var vertBuffer = gl.createBuffer(); | |
var vertArray = new Float32Array(verts); | |
var fsize = vertArray.BYTES_PER_ELEMENT; | |
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer); | |
gl.bufferData(gl.ARRAY_BUFFER, vertArray, gl.STATIC_DRAW); | |
var vertLoc = gl.getAttribLocation(program, "a_vertex"); | |
gl.vertexAttribPointer(vertLoc, 2, gl.FLOAT, false, fsize * 5, 0); | |
gl.enableVertexAttribArray(vertLoc); | |
// -- offset for color buffer | |
var colorLoc = gl.getAttribLocation(program, "a_color"); | |
gl.vertexAttribPointer(colorLoc, 3, gl.FLOAT, false, fsize * 5, fsize * 2); | |
gl.enableVertexAttribArray(colorLoc); | |
glLayer.redraw(); | |
function drawingOnCanvas(canvasOverlay, params) { | |
if (gl == null) return; | |
gl.clear(gl.COLOR_BUFFER_BIT); | |
pixelsToWebGLMatrix.set([2 / canvas.width, 0, 0, 0, 0, -2 / canvas.height, 0, 0, 0, 0, 0, 0, -1, 1, 0, 1]); | |
gl.viewport(0, 0, canvas.width, canvas.height); | |
var pointSize = Math.max(leafletMap.getZoom() - 4.0, 1.0); | |
gl.vertexAttrib1f(gl.aPointSize, pointSize); | |
// -- set base matrix to translate canvas pixel coordinates -> webgl coordinates | |
mapMatrix.set(pixelsToWebGLMatrix); | |
var bounds = leafletMap.getBounds(); | |
var topLeft = new L.LatLng(bounds.getNorth(), bounds.getWest()); | |
var offset = LatLongToPixelXY(topLeft.lat, topLeft.lng); | |
// -- Scale to current zoom | |
var scale = Math.pow(2, leafletMap.getZoom()); | |
scaleMatrix(mapMatrix, scale, scale); | |
translateMatrix(mapMatrix, -offset.x, -offset.y); | |
// -- attach matrix value to 'mapMatrix' uniform in shader | |
gl.uniformMatrix4fv(u_matLoc, false, mapMatrix); | |
gl.drawArrays(gl.TRIANGLES, 0, numPoints); | |
} | |
// Returns a random integer from 0 to range - 1. | |
function randomInt(range) { | |
return Math.floor(Math.random() * range); | |
} | |
// -- converts latlon to pixels at zoom level 0 (for 256x256 tile size) , inverts y coord ) | |
// -- source : http://build-failed.blogspot.cz/2013/02/displaying-webgl-data-on-google-maps.html | |
function LatLongToPixelXY(latitude, longitude) { | |
var pi_180 = Math.PI / 180.0; | |
var pi_4 = Math.PI * 4; | |
var sinLatitude = Math.sin(latitude * pi_180); | |
var pixelY = (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (pi_4)) * 256; | |
var pixelX = ((longitude + 180) / 360) * 256; | |
var pixel = { x: pixelX, y: pixelY }; | |
return pixel; | |
} | |
function translateMatrix(matrix, tx, ty) { | |
// translation is in last column of matrix | |
matrix[12] += matrix[0] * tx + matrix[4] * ty; | |
matrix[13] += matrix[1] * tx + matrix[5] * ty; | |
matrix[14] += matrix[2] * tx + matrix[6] * ty; | |
matrix[15] += matrix[3] * tx + matrix[7] * ty; | |
} | |
function scaleMatrix(matrix, scaleX, scaleY) { | |
// scaling x and y, which is just scaling first two columns of matrix | |
matrix[0] *= scaleX; | |
matrix[1] *= scaleX; | |
matrix[2] *= scaleX; | |
matrix[3] *= scaleX; | |
matrix[4] *= scaleY; | |
matrix[5] *= scaleY; | |
matrix[6] *= scaleY; | |
matrix[7] *= scaleY; | |
} | |
</script> | |
</body> | |
</html> |