heatmap.js icon indicating copy to clipboard operation
heatmap.js copied to clipboard

Init Error: Failed to execute 'getImageData' on 'CanvasRenderingContext2D'

Open CroniD opened this issue 8 years ago • 7 comments

Hi there,

I try to use heatmap.js with the leaflet plugin and testing the code below in a HTML file which I load locally. Once I open the file in my browser (latest Chrome), I got the error:

Uncaught DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
    at _getColorPalette (https://unpkg.com/[email protected]:267:23)
    at Canvas2dRenderer (https://unpkg.com/[email protected]:354:21)
    at Heatmap (https://unpkg.com/[email protected]:649:24)
    at Object.create (https://unpkg.com/[email protected]:714:12)
    at e.onAdd (https://unpkg.com/[email protected]/leaflet-heatmap.js:64:30)
    at e._layerAdd (https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.js:6:9138)
    at e.whenReady (https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.js:6:4302)
    at e.addLayer (https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.js:6:9455)
    at e.addTo (https://unpkg.com/[email protected]/leaflet-heatmap.js:74:11)
    at file:///D:/odin/workspace/library/tmp/leaflet-heatmap01.html:96:17

Used heatmap.js 2.0.5 & leaflet-heatmap 1.0.0 (both from npm via https://unpkg.com) with leaflet 1.0.3.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<script src="https://unpkg.com/[email protected]"></script>

		<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.css" />
		<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.js"></script>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-providers/1.1.15/leaflet-providers.min.js"></script>

		<script src="https://unpkg.com/[email protected]"></script>

		<style>
			#layer-comp-poc-leaflet {
				//width: 500px;
				height: 650px;
			}
		</style>
	</head>
	<body>
		<div id="layer-comp-poc-leaflet"></div>
		<script>
			// data
			var data = [
				{ label: 'Berlin',      lat: 52.50722, long: 13.14583, heatValue: Math.random() },
				{ label: 'Stuttgart',   lat: 48.783,   long: 9.183,    heatValue: Math.random() },
				{ label: 'Munich',      lat: 48.133,   long: 11.567,   heatValue: Math.random() },
				{ label: 'Potsdam',     lat: 52.4,     long: 13.067,   heatValue: Math.random() },
				{ label: 'Bremen',      lat: 53.083,   long: 8.8,      heatValue: Math.random() },
				{ label: 'Hamburg',     lat: 53.56528, long: 10.00139, heatValue: Math.random() },
				{ label: 'Wiesbaden',   lat: 50.08,    long: 8.24,     heatValue: Math.random() },
				{ label: 'Hanover',     lat: 52.367,   long: 9.717,    heatValue: Math.random() },
				{ label: 'Mainz',       lat: 50.0,     long: 8.267,    heatValue: Math.random() },
				{ label: 'Saarbrücken', lat: 49.233,   long: 7.0,      heatValue: Math.random() },
				{ label: 'Schwerin',    lat: 53.63333, long: 11.41667, heatValue: Math.random() },
				{ label: 'Düsseldorf',  lat: 51.233,   long: 6.783,    heatValue: Math.random() },
				{ label: 'Dresden',     lat: 51.033,   long: 13.733,   heatValue: Math.random() },
				{ label: 'Magdeburg',   lat: 52.13333, long: 11.61667, heatValue: Math.random() },
				{ label: 'Kiel',        lat: 54.333,   long: 10.133,   heatValue: Math.random() },
				{ label: 'Erfurt',      lat: 50.98333, long: 11.03333, heatValue: Math.random() }
			];
			// compute center (by arithmetic mean)
			var center = [];
			data.forEach(function (e, i) {
				if (center.length === 0) {
					center.push(e.lat);
					center.push(e.long);
				} else {
					center[0] += e.lat;
					center[1] += e.long;
				}
			});
			center[0] /= data.length;
			center[1] /= data.length;
			console.log(center[0] + ', ' + center[1]);
			// create a leaflet map
			var map = L.map('layer-comp-poc-leaflet', {
				center: L.latLng(center[0], center[1]),
				zoom: 6,
				minZoom: 6,
				maxZoom: 12,
				maxBounds: L.latLngBounds(L.latLng(46.88647742, 4.53186035), L.latLng(55.67448697, 16.04553223)),
			});
			// add tile layers
			L.tileLayer.provider('CartoDB.DarkMatterNoLabels').addTo(map);
			L.tileLayer.provider('OpenMapSurfer.AdminBounds').addTo(map);
			// add markers with popup
			data.forEach(function (e) {
				L.marker(L.latLng(e.lat, e.long), {
					title: e.label,
					alt: e.label,
					attribution: 'Marker coordinates &copy; <a href="https://en.wikipedia.org/wiki/Main_Page">Wikipedia</a>, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
				})
					//.bindPopup(e.label)
					.bindPopup('<b>' + e.label + '</b><br>latitude: ' + e.lat + '<br>longitude: ' + e.long)
					.addTo(map);
			});
			// add heatmap layer
			var heatmapCfg = {
				radius: 2,
				maxOpacity: 0.8,
				scaleRadius: true,
				useLocalExtrema: false,
				latField: 'lat',
				lngField: 'long',
				valueField: 'heatValue'
			};
			var heatmapLayer = new HeatmapOverlay(heatmapCfg);
			heatmapLayer.setData({
				min: 0,
				max: 1,
				data: data
			});
			heatmapLayer.addTo(map);

			map.eachLayer(function (layer) {
				console.log(layer);
			});
			console.log('-----------------------');
		</script>
	</body>
</html>

Any help is greatly appreciated.

Best regards

CroniD avatar Jun 14 '17 12:06 CroniD

Same here, did you manage to find a solution?

thomasMary avatar Feb 07 '18 07:02 thomasMary

Hello @thomasMary ,

unfortunately not. As far as I know, a security mechanism causes this error in chrome-based browsers. So, unless the author changes the rendering method (e.g. using SVG instead of Canvas), this lib won't work. :(

Best regards

CroniD avatar Feb 08 '18 08:02 CroniD

@CroniD , @thomasMary in order to solve that serve your html file from a HTTP server (instead of file://. file:// causes CORS issues w/ canvas ). Please re-open if that doesn't work for you

pa7 avatar Feb 08 '18 09:02 pa7

Hello @pa7 ,

the example above is loaded via a local HTTP server (see attached screenshot). While it is true that the file protocol has different security mechanism, it's not the reason why it doesn't work. The Chrome Dev team introduce an exception some months ago. Basically if you try to create an canvas and modify it (as the Lib does in https://github.com/pa7/heatmap.js/blob/master/src/renderer/canvas2d.js), it's considered to be "tainted". The error message is missleading tbh and it's true for chrome based browsers only. You can read more about this here: https://bugs.chromium.org/p/chromium/issues/detail?id=294129

It's not exactly the same issue, but the implications and consequences are the same.

Best regards

BTW: I can't re-open the issue. :( headmap_tainted_error_local_http_server

CroniD avatar Feb 08 '18 13:02 CroniD

@CroniD oh that's interesting! could you try to host heatmap.js on your local webserver? that would help to confirm that it's really not a CORS issue. Also, do the heatmap.js website demos work for you? ( https://www.patrick-wied.at/static/heatmapjs/example-minimal-config.html )

pa7 avatar Feb 09 '18 05:02 pa7

Hello @pa7 ,

thank you for re-opening the issue. I tested it and it won't work (loading from local webserver and the demo page). Same error message. :(

BTW: The browser that I use to test is Slimjet (chromium-based). It uses Chromium 63.0.3239.52 at the moment.

Best regards

CroniD avatar Feb 09 '18 08:02 CroniD