I'm trying to build a custom bing maps v8 API pushpin combining text and a small uri data image, but I get an 'Uncaught InvalidStateError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The HTMLImageElement provided is in the 'broken' state.' error on the browser console. Any ideas?
MapOptions = { credentials: 'mycredentials', mapTypeId: Microsoft.Maps.MapTypeId.road, zoom: 10, showMapTypeSelector: true, enableInertia: false, enableClickableLogo: false, disableKeyboardInput: true, navigationBarMode: navigationBarMode.minified, showLocateMeButton: true };
map = new Microsoft.Maps.Map(document.getElementById("MapDIV"), MapOptions);
LayerShapes = new Microsoft.Maps.Layer();
LayerShapes.setZIndex(101);
map.layers.insert(LayerShapes);
var svgTemplate = '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml"> <p>TEXT</p> {htmlContent} </div></foreignObject></svg>';
var customHtml = ' <image width="32" height="32" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAcCAYAAACUJBTQAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wQbECUudScMXAAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAACGUlEQVRIx+3Wy2sTURTH8e/NTDIzaZMxadMWhyBUSheiiyo+QPpHuHIhdOfSP8GlbkXEhTv/gNau3LgRurEIUqlWU2ubh7evPEg6NjOZJHNdlIgLo11YRcj5A84Hfpx7zxFKKcUJlw7gOM6JAVLKIwTg4avbfxy4c/UJABH+Qg2QAfKfI98f48vc/CCuATJA/iEilFKq3/q98XTk2I0W5qp916/41SHhOM6xoIW5KlLK/t/K6oNbwlAdknELYSZpxTMkxrO4XoCUUv0O6gHlYkjHWxF+yyWTsKit57CGbbTMGSJWepTh05PIRof3mxLNjNP0Pdp+i9ziIyGl7BtFD1hdOqRdei5ijW2shkSvS8LAJTM2gh4JiWzvFNksFdAsA3s0Ram4TrtZJxnXCLwKWSF+CvWAt89czmffiEQ0gGYZzSuTX3tNx60Q1Pcxwyb67JUL7Jb38VsdojETz2ux8W6JqG6iJaOoGLTr98WP0fWAsZgQ849v8mnZYeriLNinwAup722RsW12cysYiRT62voGwymbbsQgMZREcMD1yzN4nkctrNEV4HbrTKeFKNeOJlFKiXtwV2ganJvKkF8rsvxiEd8P0FSTiXQa2wxJxEz2yl/QA2Mc2Qihq7NdqdE5rJAc2ufsZBbTiIIGXWXTVeCIa0glMQwh8vl7hMDHD5+Zmb7E16ZPtVrFilnsFLY42CngTDhEohbfALpF/s+4JwbyAAAAAElFTkSuQmCC"/> ';
var PinPos = new Microsoft.Maps.Location(Lat, Lng);
pin = new Microsoft.Maps.Pushpin(PinPos, { icon: svgTemplate.replace('{htmlContent}', customHtml), width: 32});
LayerShapes.add(pin);
I came across the same issue.
It happens because it tries to draw pushpin icons with unloaded images.
The general idea is to preload images in JS code before using it in pushpin icons.
I solved it by this way:
I preload images to array
I used canvas instead of 'svg'
var imageArray = [];
var imagesFiles = ['home_16.png', 'home_16_done.png', 'not existing.png'];
function preLoadImages(callback) {
var filesCount = imagesFiles.length;
for (var i = 0; i < imagesFiles.length; i++) {
var imageObj = new Image();
imageObj.src = '/images/' + imagesFiles[i];
imageObj.name = imagesFiles[i];
imageObj.onload = (function (img) {
imageArray.push({ name: img.target.name, object: img.target });
filesCount--;
if (filesCount === 0) {
if (callback)
callback(imageArray);
}});
imageObj.onerror = function () { //even image is not loaded
filesCount--;
if (filesCount === 0) {
if (callback)
callback(imageArray);
}};
}
}
....
pushpin = new Microsoft.Maps.Pushpin(location,
{ icon: createCanvasContent(imageArray[i], parameters[])
.....
function createCanvasContent(img, parameters){
var c = document.createElement('canvas');
c.width = img.width + 2;
c.height = img.height + 2;
var context = c.getContext('2d');
context.fillStyle = bgrcolor;
context.fillRect(0, 0, 18, 18);
c.style.opacity = '0.8';
//Draw image
context.drawImage(img, 1, 0, c.width - 2, c.height - 2);
return c.toDataURL();
}
You can use all features from canvas tag like
context.textAlign = "center";
context.strokeText(text, x, y);
context.fillStyle = 'white';
context.fillText(text, x, y);
Related
I had an image of Canada and manually recolored its pixels to shades of blue. I then tried to make a script that would do the same(into red) without me going through all the hassle.
But for some reason, the canvas simply repaints the same image of the blue Canada in Mozilla and Chrome(sometimes not even draws it).
Mozilla says accessing ImageData is a security error. What did I get wrong and how do I fix it?
Snippet right here:
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
var imgPath = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Copper_sulfate.jpg/234px-Copper_sulfate.jpg";
var imgObj = new Image();
imgObj.crossOrigin = "anonymous";
imgObj.src = imgPath;
imgObj.onload = function() {
context.drawImage(imgObj, 0, 0);
map = context.getImageData(0, 0, 234, 240);
imagedata = map.data;
for (p = 0; p < imagedata.length; p += 4) {
imagedata[p] = imagedata[p + 2];
imagedata[p + 2] = 0;
imagedata[p + 3] = 255;
};
context.putImageData(map, 0, 0);
};
<img id="Canada" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Copper_sulfate.jpg/234px-Copper_sulfate.jpg" />
<canvas id='canvas' width='234px' height='240px'></canvas>
I cannot load/ display image created with jquery on HTML5 canvas but it works when image object is created as = new Image();
In particular, using this script as example: https://www.html5canvastutorials.com/advanced/html5-canvas-invert-image-colors-tutorial/
but substituting img source with:
imageObj.src = 'https://demo.boundlessgeo.com/geoserver/ows?&service=WMS&request=GetMap&layers=nasa%3Abluemarble&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&width=256&height=256&srs=EPSG%3A3857&bbox=0,2504688.542848655,2504688.5428486555,5009377.085697314';
and adding attribute: imageObj.crossOrigin = "Anonymous";
just after: var imageObj = new Image();
it allows for editing function to run and the image draws on canvas.
However, by creating the image with the following jquery, the image is not displayed in canvas and no errors are reported in the console:
var imageObj = $("<img />",
{
crossOrigin: "Anonymous",
src: "https://demo.boundlessgeo.com/geoserver/ows?&service=WMS&request=GetMap&layers=nasa%3Abluemarble&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&width=256&height=256&srs=EPSG%3A3857&bbox=0,2504688.542848655,2504688.5428486555,5009377.085697314"
});
Am I missing a step that binds img to document or is it something else?
In order to change the image creation in the example code you forgot the load event. Hence, your code should be:
var imageObj = $("<img />", {
crossOrigin: "Anonymous",
src: "https://demo.boundlessgeo.com/geoserver/ows?&service=WMS&request=GetMap&layers=nasa%3Abluemarble&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&width=256&height=256&srs=EPSG%3A3857&bbox=0,2504688.542848655,2504688.5428486555,5009377.085697314"
}).on('load', function (e) {
drawImage(this);
});
var imageObj = $("<img />", {
crossOrigin: "Anonymous",
src: "https://demo.boundlessgeo.com/geoserver/ows?&service=WMS&request=GetMap&layers=nasa%3Abluemarble&styles=&format=image%2Fjpeg&transparent=false&version=1.1.1&width=256&height=256&srs=EPSG%3A3857&bbox=0,2504688.542848655,2504688.5428486555,5009377.085697314"
}).on('load', function (e) {
drawImage(this);
});
function drawImage(imageObj) {
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = 69;
var y = 50;
context.drawImage(imageObj, x, y);
var imageData = context.getImageData(x, y, imageObj.width, imageObj.height);
var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
// red
data[i] = 255 - data[i];
// green
data[i + 1] = 255 - data[i + 1];
// blue
data[i + 2] = 255 - data[i + 2];
}
// overwrite original image
context.putImageData(imageData, x, y);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="myCanvas" width="578" height="400"></canvas>
I am using Leaflet to place a wms tileLayer to the map.
The images in the tiles do not have a xyz coordinates.
After loading the images I would like the images to be converted into a canvas.
I tried several things.
Adding the wms tileLayer to the map and on load running a script to create the canvas. However, of course, this runst the script way too much.
Besides that, my map is fully loaded with the images in stead of only there where the wms layer is on the map.
it looks a bit like this:
this.currentPhoto = L.tileLayer.wms(geoURL), {
layers: layerName,
format: 'image/png',
opacity: 1,
styles: '',
transparent: true,
attribution: "",
version: "2.0.0",
srs: "EPSG:4326"
}).addTo(map).bringToFront();
this.currentPhoto.on('tileload', function(e) {
setCanvasTiles(e);
});
setCanvasTiles: function(e) {
var that = this;
var url = e.url;
var tiles = HKV.satPhotoView.currentPhoto._tiles;
this.canvasTiles = new L.TileLayer.Canvas({
minZoom: 4,
maxZoom: 14,
attribution: '',
tms: true,
opacity: 0.8,
noWrap: true,
unloadInvisibleTiles: true,
reuseTiles: false,
transparent: true
});
this.canvasTiles.drawTile = function(canvas, tilePoint, zoom) {
that.drawCanvasTile(canvas, tilePoint, zoom, url);
};
this.canvasTiles.addTo(this.mapInfo).bringToFront();
},
drawCanvasTile: function(canvas, tilePoint, zoom, url) {
var that = this;
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function(){
var height = 256;
var width = 256;
ctx.drawImage(img,0,0);
imageData = ctx.getImageData(0, 0, 256, 256);
$(canvas).data("origImgData",imageData);
//color the pixels of the canvas
};
img.src = url;
},
Like I said, this script runs to much, so I think it is best to build an object with the URL of the image and the tilePoints so I can match these to the tilePoints of the Canvas TileLayer.
Is there anybody out there who did this before, or can help me out?
I am using Backbone, require and jquery next to leaflet
I found a solution.
When requesting the ._tiles from the wms layer, you will have an object with the keys of the pixelPosition. looking something like:
tiles = {
"127:47": {data},
"127:48": {data},
}
when setting the canvas you will receive the layerPoints.
Using the layerpoints I can get the image from the tile and use this.
var newKey = tilePoint.x + ":" + tilePoint.y;
var url = tiles[newKey].src;
So now I have the url. I can load the canvas.
Full code here:
this.currentPhoto = L.tileLayer.wms(geoURL), {
layers: layerName,
format: 'image/png',
opacity: 1,
styles: '',
transparent: true,
attribution: "",
version: "2.0.0",
srs: "EPSG:4326"
}).addTo(map).bringToFront();
var test = setTimeout(function() {
that.setCanvasTiles();
}, 500);
setCanvasTiles: function() {
var that = this;
var tiles = this.currentPhoto._tiles;
this.canvasTiles = L.tileLayer.canvas({
tms: true,
opacity: 0.8,
noWrap: true,
unloadInvisibleTiles: true,
reuseTiles: false,
transparent: true
});
this.canvasTiles.drawTile = function(canvas, tilePoint, zoom) {
var newKey = tilePoint.x + ":" + tilePoint.y;
var url = tiles[newKey].src;
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function(){
var height = 256;
var width = 256;
ctx.drawImage(img,0,0);
var imageData = ctx.getImageData(0, 0, 256, 256);
$(canvas).data("origImgData", imageData);
console.log(imageData);
};
img.src = url;
};
this.canvasTiles.addTo(this.map).bringToFront();
this.map.removeLayer(this.currentPhoto);
},
I am trying to convert html into a pdf client side using jspdf.
The code I have currently is this: (most of this is for pagebreaks
$(document).ready(function() {
$("#runpdf").click(function(event) {
var partsec = $("main_body page1");
html2canvas(document.body, {
logging: true,
profile: true,
allowTaint: true,
letterRendering: true,
onrendered: function(canvas) {
var imageData = canvas.toDataURL("image/jpeg");
var image = new Image();
image = Canvas2Image.convertToJPEG(canvas);
var doc = new jsPDF();
doc.page=1;
//FIRST PAGE****************************************
doc.addImage(imageData, 'JPEG', -115, 5, 440 , 875);
var croppingYPosition = 1250;
count = (image.height) / 3300;
for (var i =1; !(i >= count); i++) {
doc.addPage();
var sourceX = 0;
var sourceY = croppingYPosition;
var sourceWidth = image.width;
var sourceHeight = 1100;
var destWidth = 1600;
var destHeight = 1090;
var destX = -450
var destY = 0;
var canvas1 = document.createElement('canvas');
canvas1.setAttribute('height', 1200);
canvas1.setAttribute('width', destWidth);
var ctx = canvas1.getContext("2d");
ctx.drawImage(image,
sourceX,
sourceY,
sourceWidth,
sourceHeight,
destX,
destY,
destWidth,
destHeight);
var image2 = new Image();
image2 = Canvas2Image.convertToJPEG(canvas1);
image2Data = image2.src;
doc.addImage(image2Data, 'JPEG', 12, 10);
croppingYPosition += 1230;
}
doc.save('test.pdf');
}
});
});
});
However, I would like to, rather than using arbitrary, hard-coded pixel values for setting pagebreaks, have it search for a specific html tag, id, class, or some other identifier to search for at which to make a page break. For example, The code looks down the html until it finds something of class .pagebreak at which point it takes everything above it and stretches it to fit a pdf page (I can do the stretchy part, I just need help with the actual page break) and then looks right after the page break and starts over. Hope this made sense. Thanks!
Is it possible to use canvas.toDataURL() in Adobe AIR?
When I try I get the following error:
Error: SECURITY_ERR: DOM Exception 18
Adobe AIR enforces same origin for images used in the canvas API. Once you've used something from another domain in your canvas, you can't get pixel data back out of it. However, you can make use of the Loader class to get the pixel data, and convert it into a canvas ImageData.
For example:
function getDataURL(url, callback) {
var loader = new air.Loader();
loader.contentLoaderInfo.addEventListener(air.Event.COMPLETE, function(event) {
var bitmapData = loader.content.bitmapData;
var canvas = document.createElement('canvas');
canvas.width = bitmapData.width;
canvas.height = bitmapData.height;
var context = canvas.getContext('2d');
var imageData = context.createImageData(canvas.width, canvas.height);
var dst = imageData.data;
var src = bitmapData.getPixels(bitmapData.rect);
src.position = 0;
var i = 0;
while (i < dst.length) {
var alpha = src.readUnsignedByte();
dst[i++] = src.readUnsignedByte();
dst[i++] = src.readUnsignedByte();
dst[i++] = src.readUnsignedByte();
dst[i++] = alpha;
}
context.putImageData(imageData, 0, 0);
callback(canvas.toDataURL());
});
loader.load(new air.URLRequest(url));
}
window.addEventListener('load', function() {
getDataURL('http://www.google.com/images/logo.gif', function(dataURL) {
air.trace(dataURL);
});
}, false);