I'm trying create a webpage using open layers 7 that allows users to load a map then zoom, rotate or change the tilelayer being used as the bg.
Once day they can hit a button which should copy their map to an html canvas. I am trying to use html2canvas for this process.
So, the script works as expected for 2 of the basemaps but for the others i don't get any output when trying to copy the map to the canvas.
I believe this is an issue with a tainted canvas due to where the images are being hosted maybe? Any help on understandig what's going on and how I can fix it so I can copy any of the map backgrounds would be greatly appreciated.
HTML-
<button onclick="duplicateMap()">Copy Map</button>
<div
id="mapBox">
<div id="map" class="map"></div>
<div id="rotationContainer">
0 <input id="rotationSlider" type="range" min="0" max="360" value="0"> 360
<br>
</div>
<input id="zoomSlider" type="range" orient="vertical" min="10" max="18" value="13" />
</div>
<div id="mapCopy"></div>
<div id="baseMapSelect">
<h4 style="margin:0px; text-decoration: underline;">Select your map style</h4>
</div>
CSS-
button{margin: 10px 0}
#mapBox{
position: absolute;
width: 40%;
}
#map {
width: 300px;
height: 300px;
border: 2px solid black;
}
#mapCopy{
position:absolute;
top: 50px;
left: 450px;
width: 40%;
float: right;
}
#baseMapSelect {
position: relative;
top: 330px;
}
input[type=range][orient="vertical"] {
writing-mode: bt-lr;
/* IE */
-webkit-appearance: slider-vertical;
/* Chromium */
width: 8px;
min-height: 175px;
padding: 0 5px;
}
#zoomSlider {
position: absolute;
top: 0px;
left: 315px;
height: 280px;
margin: 10px;
}
#rotationContainer{
position: relative;
left: 5px;
}
JS-
window.onload = init;
// set some variables
var selectedBaseMap = "OSMStandard";
var currentLonLat = [-63.5859487, 44.648618]; //Halifax Lon/Lat
var defaultZoom = 12;
var defaultRotation = 0;
var rotationSlider = document.getElementById("rotationSlider");
var zoomSlider = document.getElementById("zoomSlider");
mapCenter = ol.proj.fromLonLat(currentLonLat); //Converts Lon/Lat to center
function init() {
// setup the map
const map = new ol.Map({
view: new ol.View({
center: mapCenter,
zoom: defaultZoom,
rotation: defaultRotation,
}),
target: "map",
});
// ROTATION //
rotationSlider.oninput = function () {
map.getView().setRotation(degreesToRads(this.value));
};
// ZOOM //
zoomSlider.oninput = function () {
map.getView().setZoom(this.value);
};
map.getView().on("change:rotation", function (event) {
deg = radsToDegrees(event.target.getRotation());
console.log(deg);
if (deg > 360) {
deg = deg - 360;
}
rotationSlider.value = deg;
});
/* Base Map Layer */
const openStreetMapStandard = new ol.layer.Tile({
source: new ol.source.OSM(),
visible: true,
title: "OSMStandard",
});
const openStreetMapHumanitarian = new ol.layer.Tile({
source: new ol.source.OSM({
url: "https://{a-c}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
}),
visible: false,
title: "OSMHumanitarian",
});
const stamenToner = new ol.layer.Tile({
source: new ol.source.XYZ({
url: "https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png",
attributions:
'Map tiles by <a href="http://stamen.com">Stamen Design</a>, \
under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. \
Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.',
}),
visible: false,
title: "StamenToner",
});
const stamenTerrain = new ol.layer.Tile({
source: new ol.source.XYZ({
url: "https://stamen-tiles.a.ssl.fastly.net/terrain/{z}/{x}/{y}.jpg",
attributions:
'Map tiles by <a href="http://stamen.com">Stamen Design</a>, \
under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. \
Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.',
}),
visible: false,
title: "StamenTerrain",
});
const stamenWaterColor = new ol.layer.Tile({
source: new ol.source.XYZ({
url: "https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg",
attributions:
'Map tiles by <a href="http://stamen.com">Stamen Design</a>, \
under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. \
Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.',
}),
visible: false,
title: "StamenWatercolor",
});
/* End Base Map Layer */
// Layer Group
const baseLayerGroup = new ol.layer.Group({
layers: [
openStreetMapStandard,
openStreetMapHumanitarian,
stamenToner,
stamenTerrain,
stamenWaterColor,
],
});
map.addLayer(baseLayerGroup);
// load the base maps into radio buttons for user selection
var baseMapSelect = document.getElementById("baseMapSelect");
baseLayerGroup.getLayers().forEach(function (element, index, array) {
var baseLayerTitle = element.get("title");
var html = "";
if (baseLayerTitle == selectedBaseMap) {
html +=
'<input type="radio" id="baseSelect' +
(index + 1) +
'" class="baseLayerRadio" name="baseLayerSelect" value="' +
baseLayerTitle +
'" onchange="changeBaseMap(this)" checked>';
} else {
html +=
'<input type="radio" id="baseSelect' +
(index + 1) +
'" class="baseLayerRadio" name="baseLayerSelect" value="' +
baseLayerTitle +
'" onchange="changeBaseMap(this)">';
}
html +=
'<label for="baseSelect' +
(index + 1) +
'">' +
baseLayerTitle +
"</label>";
var div = document.createElement("div");
div.innerHTML = html;
baseMapSelect.appendChild(div.firstChild);
});
const baseLayerElements = document.querySelectorAll(
"#baseMapSelect > input[type=radio]"
);
for (let baseLayerElement of baseLayerElements) {
//add a listener for each radio
baseLayerElement.addEventListener("change", function () {
let baseLayerElementValue = this.value;
baseLayerGroup.getLayers().forEach(function (element, index, array) {
let baseLayerTitle = element.get("title");
// if baseLayerTitle = baseLayerElementValue then setVisible = true, else setVisible = false
element.setVisible(baseLayerTitle === baseLayerElementValue);
});
});
}
}
//update the currently selected basemap
function changeBaseMap(x) {
selectedBaseMap = x.value;
}
function duplicateMap() {
//create an image of map.
html2canvas(document.getElementById("map")).then(function (canvas) {
console.log(canvas);
document.getElementById('mapCopy').appendChild(canvas);
});
}
/* rotation function */
function degreesToRads(deg) {
return (deg * Math.PI) / 180;
}
function radsToDegrees(rads) {
return (rads * 180) / Math.PI;
}
and here is a JSFiddle with what I've got so far.
Thanks in advance!