问题
I want to have such an interaction with a map, that when a user selects some area on the map, an image is created from this area. I made a minimized and compiltely reproducible example in this fiddle. The most important part is this crop function:
function crop(img, sx, sy, dw, dh) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = dw;
canvas.height = dh;
ctx.drawImage(img, sx, sy, dw, dh, 0, 0, dw, dh);
return canvas.toDataURL("image/png");
}
This function creates a chunk out of selected area. This is how selection looks like:
But this is what I get as a result:
So, my chunk is totally incorrect. But what is more interesting is that on some arbitrary computers and in some arbitrary browsers chunks are correct. So, I really need to make this code cross-platform and cross-browser.
回答1:
But the problem is, it seems like from JavaScript code it is impossible to know this system/browser parameter - I mean initial page zoom level.
Actually there is window.devicePixelRatio
Just use it in place of ratio_W and ratio_H, and it starts working:
var initialZoom = devicePixelRatio;
var map = new ol.Map({
layers: [new ol.layer.Tile({
source: new ol.source.OSM()
})],
target: 'map',
view: new ol.View({
center: [-348792.96, 7170957.18],
zoom: 10
})
});
var draw = new ol.interaction.Draw({
features: new ol.Collection(),
type: "LineString",
style: function (feature) {
var style = [
new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(142,142,142,0.5)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(142,142,142,1)',
lineDash: [4,4],
width: 2
})
})
];
return style;
},
geometryFunction: function (coordinates, geom) {
if (!geom) {
geom = new ol.geom.Polygon(null);
}
var start = coordinates[0];
var end = coordinates[1];
var mapExtent = map.getView().calculateExtent(map.getSize());
var chunk = [start, [start[0], end[1]], end, [end[0], start[1]],start];
var coords = [[[mapExtent[0],mapExtent[1]],[mapExtent[0],mapExtent[3]],[mapExtent[2],mapExtent[3]],[mapExtent[2],mapExtent[1]],[mapExtent[0],mapExtent[1]]], chunk];
map.exportExtent = coordinates; // all you need
geom.setCoordinates(coords);
return geom;
},
maxPoints: 2
});
var canvas = map.getViewport().firstChild;
draw.on("drawend", function (e) {
var image = new Image(),
link = document.getElementById("export-png");
var topLeft = map.getPixelFromCoordinate(map.exportExtent[0]);
var bottomRight = map.getPixelFromCoordinate(map.exportExtent[1]);
var sx = topLeft[0];
var sy = topLeft[1];
var dw = bottomRight[0] - topLeft[0];
var dh = bottomRight[1] - topLeft[1];
image.id = "pic";
image.crossOrigin = "anonymous";
image.onload = function () {
sx = sx * initialZoom;
sy = sy * initialZoom;
dw = dw * initialZoom;
dh = dh * initialZoom;
link.href = crop(image, sx, sy, dw, dh);
link.click();
};
image.src = canvas.toDataURL("image/png");
});
map.addInteraction(draw);
function crop(img, sx, sy, dw, dh) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = dw;
canvas.height = dh;
ctx.drawImage(img, sx, sy, dw, dh, 0, 0, dw, dh);
return canvas.toDataURL("image/png");
}
<link href="https://openlayers.org/en/v4.6.4/css/ol.css" rel="stylesheet"/>
<script src="https://openlayers.org/en/v4.6.4/build/ol-debug.js"></script>
<div id="map" class="map"></div>
<a id="export-png" class="btn" download="map.png"> </a>
Snippet comes from @dube's answer, and I modified image.onload only and stored initialZoom in the first line.
回答2:
I would have guessed that you mixed projections. But that was not the bug, you simply mixed up some variables. It's the unecessary complicated way of storing the selection extent.
coordinate of geometryFunction already has the extent, defined as two coordinates. Just use those and remove the loop. No idea why it's there in the first place.
var map = new ol.Map({
layers: [new ol.layer.Tile({
source: new ol.source.OSM()
})],
target: 'map',
view: new ol.View({
center: [-348792.96, 7170957.18],
zoom: 10
})
});
var draw = new ol.interaction.Draw({
features: new ol.Collection(),
type: "LineString",
style: function (feature) {
var style = [
new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(142,142,142,0.5)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(142,142,142,1)',
lineDash: [4,4],
width: 2
})
})
];
return style;
},
geometryFunction: function (coordinates, geom) {
if (!geom) {
geom = new ol.geom.Polygon(null);
}
var start = coordinates[0];
var end = coordinates[1];
var mapExtent = map.getView().calculateExtent(map.getSize());
var chunk = [start, [start[0], end[1]], end, [end[0], start[1]],start];
var coords = [[[mapExtent[0],mapExtent[1]],[mapExtent[0],mapExtent[3]],[mapExtent[2],mapExtent[3]],[mapExtent[2],mapExtent[1]],[mapExtent[0],mapExtent[1]]], chunk];
map.exportExtent = coordinates; // all you need
geom.setCoordinates(coords);
return geom;
},
maxPoints: 2
});
var canvas = map.getViewport().firstChild;
draw.on("drawend", function (e) {
var image = new Image(),
link = document.getElementById("export-png");
var topLeft = map.getPixelFromCoordinate(map.exportExtent[0]);
var bottomRight = map.getPixelFromCoordinate(map.exportExtent[1]);
var sx = topLeft[0];
var sy = topLeft[1];
var dw = bottomRight[0] - topLeft[0];
var dh = bottomRight[1] - topLeft[1];
image.id = "pic";
image.crossOrigin = "anonymous";
image.onload = function () {
var ratio_W = canvas.clientWidth / image.naturalWidth;
var ratio_H = canvas.clientHeight / image.naturalHeight;
//with or without scaling image chunk is incorrect
sx = sx * ratio_W;
sy = sy * ratio_H;
dw = dw * ratio_W;
dh = dh * ratio_H;
// console.log(sx, sy, dw, dh, ratio_H);
link.href = crop(image, sx, sy, dw, dh);
link.click();
};
image.src = canvas.toDataURL("image/png");
});
map.addInteraction(draw);
function crop(img, sx, sy, dw, dh) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = dw;
canvas.height = dh;
ctx.drawImage(img, sx, sy, dw, dh, 0, 0, dw, dh);
return canvas.toDataURL("image/png");
}
<link href="https://openlayers.org/en/v4.6.4/css/ol.css" rel="stylesheet"/>
<script src="https://openlayers.org/en/v4.6.4/build/ol-debug.js"></script>
<div id="map" class="map"></div>
<a id="export-png" class="btn" download="map.png"> </a>
来源:https://stackoverflow.com/questions/48962400/unable-to-make-a-chunk-from-canvas