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);
},
Related
I am using the below HTML to draw paths using paper.js. On the background we want to set a background image.
I tried to set image 'room.jpeg' which is stored locally. It loads correctly but it is not in the background. As a result it gets removed when I try drawing the path. The image should fit in the browser window.
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.14/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.15/paper-full.js"></script>
<script type="text/paperscript" canvas="canvas">
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var img1 = new Image();
//drawing of the test image - img1
img1.onload = function() {
//draw background image
ctx.drawImage(img1, 0, 0);
//draw a box over the top
ctx.fillStyle = "rgba(200, 0, 0, 0.5)";
ctx.fillRect(0, 0, 500, 500);
};
img1.src = 'https://i.imgur.com/6gU9m8O.png'; // 'room.jpeg'; amended for this demo
var path;
var currentPath = [];
var textItem = new PointText({
content: '',
point: new Point(20, 30),
fillColor: 'black',
});
function onMouseDown(event) {
// If we produced a path before, deselect it:
if (path) {
path.selected = false;
}
// Create a new path and set its stroke color to black:
path = new Path({
segments: [event.point],
strokeColor: 'black',
// Select the path, so we can see its segment points:
fullySelected: false
});
}
// While the user drags the mouse, points are added to the path
// at the position of the mouse:
function onMouseDrag(event) {
console.log('Capturing new path');
path.add(event.point);
var point = event.point;
currentPath.push(point.x + ', ' + point.y);
// Update the content of the text item to show how many
// segments it has:
textItem.content = '';
}
// When the mouse is released, we simplify the path:
function onMouseUp(event) {
var segmentCount = path.segments.length;
// console.log(currentPath.toString());
console.log('End');
var poi = prompt("Please enter your poi start and end");
console.log('Saving Paths' + poi + (currentPath.toString()));
// When the mouse is released, simplify it:
path.simplify(10);
// Select the path, so we can see its segments:
path.fullySelected = false;
var newSegmentCount = path.segments.length;
var difference = segmentCount - newSegmentCount;
var percentage = 100 - Math.round(newSegmentCount / segmentCount * 100);
textItem.content = '';
}
</script>
<canvas id="canvas" resize></canvas>
Using an img as background gives more trouble since you'll need to redraw/move it on every run.
I'd just use some css to set an background-image to the <canvas> itself:
canvas {
width: 100vw;
height: 100vh;
background-image: url('https://via.placeholder.com/500/FFFFF3?text=BG');
}
canvas {
width: 100vw;
height: 100vh;
background-image: url('https://via.placeholder.com/500/FFFFF3?text=BG');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.15/paper-full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.15/paper-core.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.14/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas" resize></canvas>
<script type="text/paperscript" canvas="canvas">
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var path;
var currentPath = [];
var textItem = new PointText({
content: '',
point: new Point(20, 30),
fillColor: 'black',
});
function onMouseDown(event) {
// If we produced a path before, deselect it:
if (path) {
path.selected = false;
}
// Create a new path and set its stroke color to black:
path = new Path({
segments: [event.point],
strokeColor: 'black',
// Select the path, so we can see its segment points:
fullySelected: false
});
}
// While the user drags the mouse, points are added to the path
// at the position of the mouse:
function onMouseDrag(event) {
//console.log('Capturing new path');
path.add(event.point);
var point = event.point;
currentPath.push(point.x + ', ' + point.y);
// Update the content of the text item to show how many
// segments it has:
textItem.content = '';
}
// When the mouse is released, we simplify the path:
function onMouseUp(event) {
var segmentCount = path.segments.length;
// console.log(currentPath.toString());
// console.log('End');
var poi = prompt("Please enter your poi start and end");
console.log('Saving Paths' + poi + (currentPath.toString()));
// When the mouse is released, simplify it:
path.simplify(10);
// Select the path, so we can see its segments:
path.fullySelected = false;
var newSegmentCount = path.segments.length;
var difference = segmentCount - newSegmentCount;
var percentage = 100 - Math.round(newSegmentCount / segmentCount * 100);
textItem.content = '';
}
</script>
I did the gif-animation loading, following the example in the documentation. However, frames are skipped and frames are displayed incorrectly.
This happens to many gif files. Please, how to fix it, does anyone know?
Here is an example error:
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
stage.add(layer);
var canvas = document.createElement('canvas');
// use external library to parse and draw gif animation
function onDrawFrame(ctx, frame) {
// update canvas size
canvas.width = frame.width;
canvas.height = frame.height;
// update canvas that we are using for Konva.Image
ctx.drawImage(frame.buffer, 0, 0);
// redraw the layer
layer.draw();
}
gifler('https://i.gifer.com/8RDg.gif').frames(canvas, onDrawFrame);
// draw resulted canvas into the stage as Konva.Image
var image = new Konva.Image({
image: canvas
});
layer.add(image);
<script src="https://unpkg.com/konva#4.0.4/konva.min.js"></script>
<script src="https://unpkg.com/gifler#0.1.0/gifler.min.js"></script>
<div id="container"></div>
You have to try to use another library. I used libgif.js to solve this issue that is an example
var width = window.innerWidth;
var height = window.innerHeight;
var templateImage = new Image();
templateImage.onload = function(){
// image has been loaded
drawKonva(templateImage);
};
templateImage.src = "https://i.gifer.com/8RDg.gif";
function drawKonva(templateImage) {
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);
var gif = new SuperGif({
gif: templateImage,
progressbar_height: 0,
auto_play: true,
loop_mode: true,
draw_while_loading: true
});
gif.load();
var gif_canvas = gif.get_canvas(); // the lib canvas
// a copy of this canvas which will be appended to the doc
var canvas = gif_canvas.cloneNode();
var context = canvas.getContext('2d');
function anim(t) { // our animation loop
context.clearRect(0,0,canvas.width,canvas.height); // in case of transparency ?
context.drawImage(gif_canvas, 0, 0); // draw the gif frame
layer.draw();
requestAnimationFrame(anim);
};
anim();
// draw resulted canvas into the stage as Konva.Image
var image = new Konva.Image({
image: canvas,
draggable: true
});
layer.add(image);
}
I am trying to display a high resolution image in browser using openlayers 5. I found an example on how to use zoomify to create image tiles and render it using openlayers map. But I am unable to use it for my own image. I am completely new to this. The question I ask may be very trivial. Please bear my ignorance.
Example code - This is the example from openlayers website. I am trying to do the same with this image.
I tried replacing the zoomifyUrl and iipUrl with my image url but it didn't work.
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/Tile.js';
import Zoomify from 'ol/source/Zoomify.js';
var imgWidth = 799;
var imgHeight = 586;
var zoomifyUrl = 'https://live.staticflickr.com/8173/7993440342_5d9c68faec_c.jpg';
var iipUrl = 'https://live.staticflickr.com/8173/7993440342_5d9c68faec_c.jpg' + '&JTL={z},{tileIndex}';
var layer = new TileLayer({
source: new Zoomify({
url: zoomifyUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
})
});
var extent = [0, -imgHeight, imgWidth, 0];
var map = new Map({
layers: [layer],
target: 'map',
view: new View({
// adjust zoom levels to those provided by the source
resolutions: layer.getSource().getTileGrid().getResolutions(),
// constrain the center: center cannot be set outside this extent
extent: extent
})
});
map.getView().fit(extent);
var control = document.getElementById('zoomifyProtocol');
control.addEventListener('change', function(event) {
var value = event.currentTarget.value;
if (value === 'iip') {
layer.setSource(new Zoomify({
url: iipUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
}));
} else if (value === 'zoomify') {
layer.setSource(new Zoomify({
url: zoomifyUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
}));
}
});
I want to achieve something like the demo in openseadragon website. After making the above code change, I get a a grid with a portion of image repeated.
To use Zoomify you need a server which supports the image served as tiles. The url you are using is a static image on flickr which OpenLayers would need to process as ImageStatic. Here is the code using the highest resolution image from flickr
var extent = [0, 0, 10000, 7328];
var resolutions = [64, 32, 16, 8, 4, 2, 1];
var layer = new ol.layer.Image({
source: new ol.source.ImageStatic({
url: 'https://live.staticflickr.com/8173/7993440342_6a1f281898_o_d.jpg',
imageExtent: extent
})
});
var map = new ol.Map({
layers: [layer],
target: 'map',
view: new ol.View({
// adjust zoom levels to those provided by the source
resolutions: resolutions,
// constrain the center: center cannot be set outside this extent
extent: extent
})
});
map.getView().fit(extent);
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<div id="map" class="map"></div>
Alternatively you could link to the url being used by OpenSeadragon. Sttrangely the zoom levels run from 8 to 14, with one tile at level 8 and a 55 x 41 grid at level 14 where the tiles in the rightmost column are 206px wide and in the bottom row 41px high. Zoomify can be used but a custom tile url function is needed to add 8 to the zoom level expected by OpenLayers.
var imgWidth = 54*256 + 206;
var imgHeight = 40*256 + 41;
var zoomifyUrl = 'https://openseadragon.github.io/example-images/duomo/duomo_files/{z}/{x}_{y}.jpg';
var layer = new ol.layer.Tile({
source: new ol.source.Zoomify({
url: zoomifyUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
})
});
layer.getSource().setTileUrlFunction(function(tileCoord) {
return zoomifyUrl.replace(
'{z}', tileCoord[0]+8
).replace(
'{x}', tileCoord[1]
).replace(
'{y}', -(tileCoord[2]+1)
);
});
var extent = [0, -imgHeight, imgWidth, 0];
var map = new ol.Map({
layers: [layer],
target: 'map',
view: new ol.View({
// adjust zoom levels to those provided by the source
resolutions: layer.getSource().getTileGrid().getResolutions(),
// constrain the center: center cannot be set outside this extent
extent: extent
})
});
map.getView().fit(extent);
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<div id="map" class="map"></div>
Looking at the result from that attempt it is apparent some tiles are only 255px instead of the standard 256 which is causing white lines to appear on the output. I added a custom tile load function to stretch 255px widths or heights to 256 (but it must not stretch tiles at the right and bottom edges which can be less than 255px).
var imgWidth = 54*256 + 206;
var imgHeight = 40*256 + 41;
var zoomifyUrl = 'https://openseadragon.github.io/example-images/duomo/duomo_files/{z}/{x}_{y}.jpg';
var layer = new ol.layer.Tile({
source: new ol.source.Zoomify({
url: zoomifyUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
})
});
layer.getSource().setTileUrlFunction(function(tileCoord) {
return zoomifyUrl.replace(
'{z}', tileCoord[0]+8
).replace(
'{x}', tileCoord[1]
).replace(
'{y}', -(tileCoord[2]+1)
);
});
var tileSize = ol.size.toSize(layer.getSource().getTileGrid().getTileSize(0));
layer.getSource().setTileLoadFunction(function(imageTile, src) {
var img = document.createElement('img');
img.onload = function() {
var width = img.naturalWidth >= tileSize[0]-1 ? tileSize[0] : img.naturalWidth;
var height = img.naturalHeight >= tileSize[1]-1 ? tileSize[1] : img.naturalHeight;
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
imageTile.getImage().src = canvas.toDataURL();
};
img.crossOrigin = 'anonymous';
img.src = src;
});
var extent = [0, -imgHeight, imgWidth, 0];
var map = new ol.Map({
layers: [layer],
target: 'map',
view: new ol.View({
// adjust zoom levels to those provided by the source
resolutions: layer.getSource().getTileGrid().getResolutions(),
// constrain the center: center cannot be set outside this extent
extent: extent
})
});
map.getView().fit(extent);
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<div id="map" class="map"></div>
Using a dzi XML parser:
var map = new ol.Map({
target: 'map',
logo: false
});
var layer = dzi.loadUrl(
'https://openseadragon.github.io/example-images/duomo/duomo.dzi',
{ attributions: 'Image © 2012, Dario Morelli' }
);
layer.on('change:source', function(evt) {
map.setView(
new ol.View({
resolutions: layer.getSource().getTileGrid().getResolutions(),
extent: layer.getExtent()
})
);
map.getView().fit(layer.getExtent(), { size: map.getSize() });
});
map.addLayer(layer);
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script>
dzi = function() {
function loadUrl ( url,
opt_options // attributions (defaults to undefined), crossOrigin (defaults to 'anonymous')
) {
var options = opt_options || {};
var crossOrigin = options.crossOrigin === undefined ? 'anonymous' : options.crossOrigin;
var layer = new ol.layer.Tile();
var last = url.lastIndexOf('.');
var path = url.slice(0, last);
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(xhr.responseText,'text/xml');
var elements = xmlDoc.getElementsByTagName('Image');
var tileSize = Number(elements[0].getAttribute('TileSize'));
var format = elements[0].getAttribute('Format');
var width = Number(elements[0].getElementsByTagName('Size')[0].getAttribute('Width'));
var height = Number(elements[0].getElementsByTagName('Size')[0].getAttribute('Height'));
var url = path + '_files/{z}/{x}_{y}.' + format;
var source = new ol.source.Zoomify({
attributions: options.attributions,
url: url,
size: [width, height],
tileSize: tileSize,
crossOrigin: crossOrigin
});
var offset = Math.ceil(Math.log(tileSize)/Math.LN2);
source.setTileUrlFunction(function(tileCoord) {
return url.replace(
'{z}', tileCoord[0] + offset
).replace(
'{x}', tileCoord[1]
).replace(
'{y}', -(tileCoord[2]+1)
);
});
layer.setExtent([0, -height, width, 0]);
layer.setSource(source);
}
xhr.send();
return layer;
}
return {
"loadUrl" : loadUrl
}
} ();
</script>
<div id="map" class="map"></div>
The above answer has two issues:
lack of support for deepzoom in zoomify
white lines in between the tiles.
I was able to figure it out. Answer: Deepzoom into OpenLayers images using zoomify
I have a problem with FabricJS objects. I would like to create a clipping area (svg object), which will clips loaded images.
I'm using clipTo method on loaded image passing it into clipping area shape object (SVG), but it does not work, because the SVG is not shape object in sense of FabricJS. Is there any way to convert the loaded SVG into FabricJS shape like Polygon or Rect?
It almost works good if I will use:
globalCompositeOperation = 'source-atop'
on the loaded image (and remove clipTo function), but if I will move/resize/rotate the image it overlaps border of the main clipping area (the biggest rect). Image should not interact with other objects outside the grey rect, especially with main clipping area.
Maybe is there a way to achieve this by playing with globalCompositeOperation?
I will be very grateful for any tips.
Here is JSFiddle of my problem:
https://jsfiddle.net/6nfdr1ng/
and here almost working version (with source-atop):
https://jsfiddle.net/ud9nev1g/
<input type="file" id="image"><br>
<canvas id="canvas" width="530" height="600"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"></script>
<script type="text/javascript">
var canvas = new fabric.Canvas('canvas', {
'selection': false
});
/**
* loading man clipping rect
*/
var clippingRect = new fabric.Rect({
width: 185,
height: 400,
fill: 'transparent',
stroke: 1,
opacity: 1,
hasBorders: false,
hasControls: false,
hasRotatingPoint: false,
selectable: false,
preserveObjectStacking: true,
objectCaching: false
});
canvas.add(clippingRect);
canvas.renderAll();
/**
* loading shape which should be another clipping area for loaded image
*/
var svgString = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 63.7 63.7"><defs><style>.cls-1{fill:#79797a;fill-rule:evenodd;}</style></defs><title>Asset 4</title><g id="Layer_2" data-name="Layer 2"><g id="kwadrat"><path class="cls-1" d="M0,0H63.7V63.7H0Z"/></g></g></svg>';
var retinaScalling = canvas.getRetinaScaling();
fabric.loadSVGFromString(svgString, function (objects, options) {
var shapeObject = fabric.util.groupSVGElements(objects, options);
shapeObject.scaleX = 0.5;
shapeObject.scaleY = 0.5;
shapeObject.set('id', 'shape');
shapeObject.scaleToWidth(clippingRect.getWidth());
shapeObject.setCoords();
shapeObject.clipTo = function (ctx) {
ctx.save();
ctx.setTransform(retinaScalling, 0, 0, retinaScalling, 0, 0);
clippingRect.render(ctx);
ctx.restore();
};
shapeObject.center();
shapeObject.setCoords();
canvas.setActiveObject(shapeObject);
canvas.add(shapeObject);
canvas.renderAll();
});
/**
* loading image
*/
document.getElementById('image').onchange = function (e) {
var reader = new FileReader();
var clippingAreaShape = canvas.getActiveObject();
reader.onload = function (event) {
var img = new Image();
img.onload = function () {
var shapeImageObject = new fabric.Image(img, {
objectCaching: false,
hasControls: true,
selectable: true
});
shapeImageObject.scaleToHeight(clippingAreaShape.getWidth());
shapeImageObject.set('id', 'shape-image');
shapeImageObject.set({
left: clippingAreaShape.left,
top: clippingAreaShape.top
});
shapeImageObject.clipTo = function (ctx) {
ctx.save();
ctx.setTransform(retinaScalling, 0, 0, retinaScalling, 0, 0);
clippingAreaShape.render(ctx);
ctx.restore();
};
// shapeImageObject.globalCompositeOperation = 'source-atop';
canvas.add(shapeImageObject);
canvas.renderAll();
canvas.setActiveObject(shapeImageObject);
shapeImageObject.setCoords();
};
img.src = event.target.result;
};
reader.readAsDataURL(e.target.files[0]);
}
</script>
I resolved my problem by combining the SVG shape into one path (without any polygons any other svg objects), then I pass the svg coords into fabircJS path. The valid SVG coords looks like that "M0,0H63.7V63.7H0Z"
fabric.loadSVGFromURL('object.svg', function (objects, options) {
let img1 = new fabric.Path(objects[0].d, {
fill: '#333',
opacity: 1,
hasBorders: true,
hasControls: true,
hasRotatingPoint: true,
selectable: true,
preserveObjectStacking: true,
objectCaching: false,
});
}
I hope it will help someone else :)
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);