I'm currently using Cesium for a mapping application, and I have a requirement to have status indicators for each item I'm plotting (for example, if the item I'm plotting is an airplane, then I need to have a fuel status indicator). I can't use Cesium's drawing tools to do this because they are drawn using geographic locations, but I need my status indicators to simply be located near the billboard and not get farther away from the billboard as users zoom in and out.
Cesium's CZML documentation states that the billboard's "image" property can take a data URI, so I figured the easiest way to handle this would be for me to create an SVG path on the fly and embed it in the image property, but when I do this in Cesium, it does not show up. For example, I tried a simple test like this:
"data:image/svg+xml,<svg viewBox='0 0 40 40' height='25' width='25'
xmlns='http://www.w3.org/2000/svg'><path fill='rgb(91, 183, 91)' d='M2.379,
14.729L5.208,11.899L12.958,19.648L25.877,6.733L28.707,9.561L12.958,25.308Z'
/></svg>"
When that didn't show up, I tried just simple HTML and text values, like this:
"data:,Hello%2C%20World!"
and:
"data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E"
but those don't show up either. I am able to get a png to show up if I put the base64 string in the data URI, as well as a path to an image stored on the server, but I really need to be able to draw custom images on the fly. I can't use a fixed set of pre-generated images set with various statuses as a hack (I can explain why if anyone wants those details :) ).
Does anyone know if there's something I'm doing wrong here, or if there is another way to accomplish what I need to do?
Edit Just wanted to add that I am using Firefox version 29 and it normally has no problem displaying the non-encoded embedded SVGs like that. Just in case, that's one of the reasons I was also trying simple HTML or text.
Edit2 I am using CZML streaming from the back end to plot my items, here is a simple test example showing where I am trying to put the image information:
{
"id":"test",
"billboard" : {
"image" : "data:image/svg+xml,<svg viewBox='0 0 40 40' height='25' width='25' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(91, 183, 91)' d='M2.379,
14.729L5.208,11.899L12.958,19.648L25.877,6.733L28.707,9.561L12.958,25.308Z'
/></svg>",
"show" : [ {"boolean" : true} ]
},
"position":{
"cartographicDegrees":[0.0, 0.0, 0.0]
},
"label":{"text":"TEST"},
}
If I put a base64 png string in there, or a path to a static image file, it works fine.
Thank you!
Simple JS code to insert SVG into cesium
// create the svg image string
var svgDataDeclare = "data:image/svg+xml,";
var svgCircle = '<circle cx="10" cy="10" r="5" stroke="black" stroke-width="3" fill="red" /> ';
var svgPrefix = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="40px" height="40px" xml:space="preserve">';
var svgSuffix = "</svg>";
var svgString = svgPrefix + svgCircle + svgSuffix;
// create the cesium entity
var svgEntityImage = svgDataDeclare + svgString;
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-80.12, 25.46),
billboard: {
image: svgEntityImage
}
});
// test the image in a dialog
$("#dialog").html(svgString );
$("#dialog").dialog({
position: {my: "left top", at: "left bottom"}
});
My method to accomplish this is to create a canvas element, add text to it, crop it and convert that result into a data URL. It works great and it's quick.
I'm not using CZML, but this is how I did it:
buildImage:function(text,text2){
var self = this;
var canvas = new Element('canvas',{width:500,height:500});
var ctx = canvas.getContext("2d");
ctx.font = "bold 16px monospace";
ctx.fillText(text, 60, 20);
ctx.fillText(text2, 60, 50);
imagedata = self.cropCanvas(canvas,ctx);
return imagedata;
},
cropCanvas:function(canvas,ctx){
ww = canvas.width;
wh = canvas.height;
imageData = ctx.getImageData(0, 0, ww, wh);
var topLeftCorner = {};
topLeftCorner.x = 9999;
topLeftCorner.y = 9999;
var bottomRightCorner = {};
bottomRightCorner.x = -1;
bottomRightCorner.y = -1;
for (y = 0; y < wh; y++) {
for (x = 0; x < ww; x++) {
var pixelPosition = (x * 4) + (y * wh * 4);
a = imageData.data[pixelPosition+3]; //alpha
if (a > 0) {
if (x < topLeftCorner.x) {
topLeftCorner.x = x;
}
if (y < topLeftCorner.y) {
topLeftCorner.y = y;
}
if (x > bottomRightCorner.x) {
bottomRightCorner.x = x;
}
if (y > bottomRightCorner.y) {
bottomRightCorner.y = y;
}
}
}
}
topLeftCorner.x -= 2;
topLeftCorner.y -= 2;
bottomRightCorner.x += 2;
bottomRightCorner.y += 2;
relevantData = ctx.getImageData(topLeftCorner.x, topLeftCorner.y, bottomRightCorner.x -topLeftCorner.x, bottomRightCorner.y - topLeftCorner.y);
canvas.width = bottomRightCorner.x - topLeftCorner.x;
canvas.height = bottomRightCorner.y - topLeftCorner.y;
ww = canvas.width;
wh = canvas.height;
ctx.clearRect(0,0,ww,wh);
ctx.putImageData(relevantData, 0, 0);
return canvas.toDataURL();
}
These are two methods of a MooTools class, but can be easily rewritten into whatever framework (or no framework) you need.
Drawing an SVG does work as I expected it to, but I believe I may have just been getting one of the size elements wrong (height/width or x, y). Whenever those values don't match up just right, the image isn't shown because it's outside of the view area I've defined for it.
Note that I never did get the simple html example work, but that's not what I needed anyway, so I didn't pursue it further.
来源:https://stackoverflow.com/questions/24869733/how-to-draw-custom-dynamic-billboards-in-cesium-js