
function makeOSMTiles(geo) {
    if (geo["latitude"] !== undefined && geo["longitude"] !== undefined &&
        geo["latitude"] >= -90 && geo["latitude"] <= 90 &&
        geo["longitude"] >= -180 && geo["longitude"] <= 180) {

        var tileServer = "https://c.tile.openstreetmap.org",
            currentVersion = 0,
            getTile = function(zoom, x, y, result) {
                return new Promise(function(resolve, reject) {
                    var img = new Image();
                    img.addEventListener("load", function() {
                        result.img = img;
                        resolve(result);
                    });
                    img.addEventListener("error", function() {
                        console.warn("Error loading tile ", {
                            "zoom":zoom,
                            "x": x,
                            "y": y
                        });
                        reject(img);
                    });
                    img.crossOrigin = "anonymous";
                    img.src = tileServer + '/' +zoom +'/' +x +'/' +y +'.png';
                });
            },
            canvas = document.createElement("canvas"),
            mapCanvas = document.createElement("canvas");
        var imgSize = 300,
            tileSize = imgSize /3;
        canvas.height = canvas.width = mapCanvas.height = mapCanvas.width = imgSize;
        var ctx = canvas.getContext("2d"),
            mapCtx = mapCanvas.getContext("2d");

        var drawPlot = function(centerX, centerY, radius) {
            centerX = tileSize *centerX +tileSize;
            centerY = tileSize *centerY +tileSize;

            ctx.putImageData(mapCtx.getImageData(0, 0, imgSize, imgSize), 0, 0);
            if (radius !== undefined) {
                ctx.beginPath();
                ctx.arc(centerX, centerY, Math.max(radius, 10), 0, 2 * Math.PI, false);
                ctx.lineWidth = 2;
                ctx.fillStyle = 'rgba(244, 146, 66, 0.4)';
                ctx.strokeStyle = 'rgba(244, 146, 66, 0.8)';
                ctx.stroke();
                ctx.fill();
            }
            if (radius === undefined || radius > 25) {
                ctx.strokeStyle = 'rgba(244, 146, 66, 1)';
                ctx.beginPath();
                ctx.moveTo(centerX -5, centerY -5);
                ctx.lineTo(centerX +5, centerY +5);
                ctx.stroke();
                ctx.moveTo(centerX +5, centerY -5);
                ctx.lineTo(centerX -5, centerY +5);
                ctx.stroke();
            }
        },
        distanceLatlonlon = function(lat, lon0, lon1) {
            lat = lat * Math.PI / 180;
            lon0 = lon0 *Math.PI / 180;
            lon1 = lon1 *Math.PI / 180;
            return Math.abs(6371e3 * Math.acos(Math.pow(Math.sin(lat), 2) +Math.pow(Math.cos(lat), 2) *Math.cos(lon1 -lon0)));
        },
        drawTiles = function(zoom, lat, lon, acc) {
            mapCtx.fillStyle = "#808080";
            mapCtx.fillRect(0, 0, imgSize, imgSize);
            ctx.fillStyle = "#808080";
            ctx.fillRect(0, 0, imgSize, imgSize);

            var nbTiles = Math.pow(2, zoom),
                px = ((lon +180) / 360) * nbTiles,
                py = (1 -Math.log(Math.tan(lat *Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180))/Math.PI) / 2 *nbTiles,
                absoluteX = Math.floor(px),
                absoluteY = Math.floor(py),
                accRadiusPx = acc ? acc * 100 /distanceLatlonlon(180/Math.PI*Math.atan(0.5*(Math.exp(Math.PI-2*Math.PI*absoluteY/nbTiles)-Math.exp(-(Math.PI-2*Math.PI*absoluteY/nbTiles)))), absoluteX / nbTiles * 360.0 - 180.0, (absoluteX +1) / nbTiles * 360.0 - 180.0) : 0,
                v = currentVersion;

            for (var i =0; i < 3; i++)
                for (var j =0; j < 3; j++)
                    getTile(zoom, absoluteX +i -1, absoluteY +j -1, { i: i, j: j, v: v }).then(function(img) {
                        if (img.v === currentVersion) {
                            mapCtx.drawImage(img.img, tileSize *img.i, tileSize *img.j, tileSize, tileSize);
                            drawPlot(px -absoluteX, py -absoluteY, accRadiusPx);
                        }
                    });
        },
        currentZoom,
        setZoom = function(val) {
            var newZoom = Math.max(4, Math.min(19, val));
            if (currentZoom !== newZoom) {
                currentVersion++;
                currentZoom = newZoom;
                drawTiles(currentZoom, Number(geo["latitude"]), Number(geo["longitude"]), Number(geo["accuracy"]));
            }
        };

        setZoom(12);

        var canvasWrapper = document.createElement("div"),
            buttonContainer = document.createElement("div"),
            zoomP = document.createElement("button"),
            zoomM = document.createElement("button");

        canvasWrapper.className = R.klass.map.container;
        canvas.className = R.klass.map.canvas;
        buttonContainer.className = R.klass.map.buttonContainer;
        zoomM.className = R.klass.map.buttonM;
        zoomP.className = R.klass.map.buttonP;

        zoomM.addEventListener("click", function() {
            setZoom(currentZoom -1);
        });
        zoomP.addEventListener("click", function() {
            setZoom(currentZoom +1);
        });

        buttonContainer.appendChild(zoomM);
        buttonContainer.appendChild(zoomP);
        canvasWrapper.appendChild(canvas);
        canvasWrapper.appendChild(buttonContainer);
        return canvasWrapper;
    }
}

