Use GIF as a source of ImageWMS on OpenLayers Map - javascript

OpenLayers version 2 seemed to display gifs without any problem.
OpenLayers: 4.6.4 (the version that I am using now) only displays the first frame of the gif: https://prnt.sc/i5xrun
Andreas Hocevar suggested a solution to this problem (https://github.com/openlayers/openlayers/issues/4133):
Use a "custom imageLoadFunction for the image source. Instead of
returning the image itself, return a canvas with a frame of the gif,
which you can get using third party libraries (e.g.
http://themadcreator.github.io/gifler/docs.html#animator::createBufferCanvas())."
I tried to use his approach but didn't manage to display the canvas on the map. Could someone either advise how to show gif on the map or how to display canvas holding that gif?
Here's the code that I used:
<script src="/js/gifler.min.js"></script>
...
var ImageSource = new ol.source.ImageWMS({
url: 'http://weather-services.telventdtn.com/basic/wms_v1/wms.wsgi?',
params: {
'REQUEST' : 'GetAnimationFile',
'VERSION' : '1.3.0',
'SERVICE' : 'WMS',
'STYLES' : '',
'LAYERS' : 'RADAR_US_CURRENT',
'CRS' : 'EPSG:900913',
'BBOX' : '-16730536.751059378,2397065.207023127,-3629841.5992064495,8198941.401981145',
'FORMAT' : 'image/gif',
'WIDTH' : '2678',
'HEIGHT' : '1186',
'FRAMEDELAY' : '20',
'TRANSPARENT': 'TRUE'
},
imageLoadFunction: function (image, src) {
var client = new XMLHttpRequest();
client.open('GET', src, true);
client.setRequestHeader('Authorization', "Basic " + btoa("login:password"));
client.responseType = "arraybuffer";
client.onload = function () {
var byteArray = new Uint8Array(this.response);
var blob = new Blob([byteArray], {type: "image/png"});
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL(blob);
gifler(imageUrl)
.get()
.then(function(animator) {
var BufferCanvas = animator.constructor.createBufferCanvas(animator._frames[0], animator.width, animator.height);
animator.animateInCanvas(BufferCanvas);
image.setImage(BufferCanvas);
});
};
client.send();
}
});
var Image = new ol.layer.Image({
name: 'precip_layer',
opacity: 0.8,
zIndex: 1,
source: ImageSource
});

The solution is in the new release of OpenLayers v4.6.5. Below is the example from their official website of how the animated WMS layer can be achieved:
https://openlayers.org/en/latest/examples/wms-time.html

I had some success animating gif downloaded through imageWMS (used ncWMS for this).
create an ImageWMS source image layer to get the animated gif from ncWMS or other WMS service that provides animated gifs.
Then I would use the gifuct-js.js library to unpack the gif in the ImageWMS "imageLoadFunction".
I then have another ImageLayer which uses an ImageCanvasSource, in which I have the canvasFunction create the canvas, which I give to the gif library functions to render on top of.
The net result is one layer to downloade the imageWMS and another layer to display it as an ImageCanvasSource. You may have to do some projection alignment, but now you can have controls to animate the gif in another layer.

Related

Leaflet Overlay Layers Control Menu with JQuery ajax Call (GeoServer WFS)

I can setup a LeafletJS map having multiple (overlay) layers and to manage their visibility with a control menu when using basic GeoJSON files, exactly like the doc example : http://leafletjs.com/examples/layers-control/.
This works fine :
var geojson_layer1 = L.geoJson.ajax("layer1.geojson", {style: style, onEachFeature:onEachFeature});
var geojson_layer2 = L.geoJson.ajax("layer2.geojson", {style: style, onEachFeature:onEachFeature});
...
geojson_layer1.addTo(map);
geojson_layer2.addTo(map);
...
var baseMaps = {
"MyMap": map
};
var overlayMaps = {
"Layer1": Layer1,
"Layer2": Layer2
};
...
L.control.layers(baseMaps, overlayMaps).addTo(map);
Now I try to do the same (still basemap + overlay layers with control) but with data coming from a GeoServer (WMS/WFS). For this I use JQuery ajax call, eg :
var xyz_Parameters = {
service: 'WFS',
version: '1.0.0',
request: 'GetFeature',
typeName: 'myworkspace:xyz',
maxFeatures: '50',
outputFormat: 'application/json',
};
var parameters_xyz = L.Util.extend(xyz_Parameters);
var URL = owsrootUrl + L.Util.getParamString(parameters_xyz);
console.log(URL);
$.ajax({ url: URL, success: function (data) {
var geojson_layer_1 = new L.geoJson(data, {style: style, onEachFeature:doNothing});
geojson_layer_1.addTo(map);
}
});
This works fine too BUT I'm unable to manage the layers visibility through menu as variable (geojson_layer_1 here) is not seen globally.
Actually my problem comes with the async/callback (normal) behavior. I'd like to know how to manager a layer object with callback.
I've seen lot of things on the web (eg : Handling ajax-request in leaflet map) but either it is not exactly what I want or I wasn't able to make them work.
Does anyone would have a (more) complete example ?
Thanks.
Tom

How to add a icon to cesium map instead of point

I want to add icon to a cesium map intead drawing a point.
Currently I doing the below code, but want to replace the point below with an actual icon. I have been looking through the cesium documentation and cannot find anything that will do this. Thanks for any suggestions
var points = scene.primitives.add(new Cesium.PointPrimitiveCollection());
points.add({
position : new Cesium.Cartesian3.fromDegrees(longitude, latitude),
color : colorDot,
outlineColor : Cesium.Color.WHITE,
outlineWidth : width
});
In Cesium, this is called a billboard. They are created in basically the same way as a point, except the image is generally loaded from a URL.
https://cesiumjs.org/Cesium/Build/Documentation/BillboardCollection.html
// Create a billboard collection with two billboards
var billboards = scene.primitives.add(new Cesium.BillboardCollection());
billboards.add({
position : new Cesium.Cartesian3(1.0, 2.0, 3.0),
image : 'url/to/image'
});
billboards.add({
position : new Cesium.Cartesian3(4.0, 5.0, 6.0),
image : 'url/to/another/image'
});
Adding to #paraquat's correct answer about Billboards: Cesium includes a "Pin Builder" that can be used to make typical map icons as billboards. Here's a demo.
var viewer = new Cesium.Viewer('cesiumContainer');
var pinBuilder = new Cesium.PinBuilder();
var url = Cesium.buildModuleUrl('Assets/Textures/maki/grocery.png');
var groceryPin = Cesium.when(pinBuilder.fromUrl(url, Cesium.Color.GREEN, 48), function(canvas) {
return viewer.entities.add({
name : 'Grocery store',
position : Cesium.Cartesian3.fromDegrees(-75.1705217, 39.921786),
billboard : {
image : canvas.toDataURL(),
verticalOrigin : Cesium.VerticalOrigin.BOTTOM
}
});
});

How can i an add image overlay depending on the options in the polygon properties in a leaflet map?

I have a multi-polygon geojson file ( mapInfo ) that has one of the properties named "field_hazards". The hazards include "gale/storm, heavy rain, thunderstorm and Freezing rain "
How can i an add image overlay depending on the field_hazard options?
Note the geojson file is got dynamically as a drupal feed. I have made the following code but it returns an empty map, the polygons are not rendered.
function getImage(d) {
return d === 'Freezing Rain' ? "http://mymap:8082/images/weather-images/43n.png" :
d === 'Thunderstorm' ? "http://mymap:8082/images/weather-images/11.png" :
d === 'Heavy Rain' ? "http://my:8082/weather-images/02n.png" :
d === 'Gale\/Storm' ? "http://mymap:8082/images/weather-images/15.png" :
"http://my:8082/images/weather-images/09.png";
}
var imageUrl = getImage(feature.properties.field_hazards);
var imageLayer = L.imageOverlay(imageUrl, imageBounds).addTo(map).bringToBack();
var boxOptions = {fillOpacity:0, opacity:0, onEachFeature: onEachBox};
//create the image interaction box
var imageBox = L.geoJson(mapInfo, boxOptions).addTo(map);
//zoom in to fit GeoJSON layer
map.fitBounds(imageBox.getBounds());
I have looked at this example " add image to rect polygon in leaflet
", but it requires me to have the image url preset as a property its self.
I got a solution from this link. Posted it in case some else is interested.
Adding image overly in leaflet map from a property

Saving camanjs image after adding new layer

I am trying to download a canvas (with canvas.toBlob()) created with camanjs after it applies a new layer. I can only get it to download the image without the applied layer. I can right click and "save as..." to get the correct image but the downloaded file is incorrect.
Caman("#myImage", function() {
var canvas = document.getElementById('myImage');
var context = canvas.getContext('2d');
this.newLayer(function() {
var imageObj = new Image();
imageObj.src = "some_image.png";
imageObj.onload = function() {
context.drawImage(imageObj);
};
});
this.render(function() {
saveCanvas();
});
});
Try the below code snippet which converts the canvas image with multiple layers to base64 and save as JPEG. If you want to save in some other format like "png" then just
give "download.download = 'image.png'" in the below code
saveCanvas(){
var download = document.createElement('a');//Create <a> tag
download.href = document.getElementById('myImage').toDataURL(); //convert canvas image with multiple layers to base64
download.download = 'image.jpeg'; // Mention the file name and format to be downloaded
download.click();// Trigger click event to downkload the image
}

Canvas has been tainted by cross-origin data via local chrome:// extension URL

I am working on a google chrome extension and I am trying to load an image that is bundled with the extension into a canvas.
var canvas = document.createElement('canvas');
canvas.width = 470;
canvas.height = 470;
var context = canvas.getContext('2d');
var image = new Image();
image.addEventListener('load', function(){
//process
});
image.src = chrome.extension.getURL("asset/gotcha.png");
When I execute the code in a content script I am getting:
Unable to get image data from canvas because the canvas has been tainted by
cross-origin data.
Is there a way to avoid this? I have successfully embedded images, audio, video and flash directly into target sites without any those issues. The resource is listed under the web_accessible_resources in the manifest file.
Based on ExpertSystem's approach I got a simple solution.
First part in the JavaScript of the background page where a canvas can be created without throwing a security exception.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.message == "convert_image_url_to_data_url") {
var canvas = document.createElement("canvas");
var img = new Image();
img.addEventListener("load", function() {
canvas.getContext("2d").drawImage(img, 0, 0);
sendResponse({data: canvas.toDataURL()});
});
img.src = request.url;
return true; // Required for async sendResponse()
}
}
)
Second part for the content script:
//#success is the callback
function local_url_to_data_url(url, success) {
chrome.runtime.sendMessage(
{message: "convert_image_url_to_data_url", url: url},
function(response) {success(response.data)}
);
}
You can't directly pass an image from your extension to a web-page's canvas without making it tainted.
This is a work-around:
Description:
You access the image from your background page (or content script).
You put it in a canvas and convert it to a dataURL.
You inject some JS code into the web-page, passing the dataURL as a string.
The injected code uses the string (dataURL) to create an image (in the context of the web-page) and draw it onto a canvas.
Sample code:
/* In `background.js` */
function injectImg(tabID, remoteCanvasID, imgPath) {
var canvas = document.createElement("canvas");
var img = new Image();
img.addEventListener("load", function() {
canvas.getContext("2d").drawImage(img, 0, 0);
var dataURL = canvas.toDataURL();
var code = [
"(function() {",
" var canvas = document.getElementById(\"" + remoteCanvasID + "\");",
" var img = new Image();",
" img.addEventListener(\"load\", function() {",
" canvas.getContext(\"2d\").drawImage(img, 0, 0);",
" });",
" img.src = \"" + dataURL + "\";",
" ",
"})();"].join("\n");
chrome.tabs.executeScript(tabID, { code: code });
});
img.src = chrome.extension.getURL(imgPath);
}
chrome.runtime.onMessage.addListener(function(msg, sender)) {
if (msg.action && (msg.action == "getImg")
&& msg.imgPath && msg.canvasID) {
injectImg(sender.tab.id, msg.canvasID, msg.imgPath);
}
});
/* In `content.js` */
chrome.runtime.sendMessage({
action: "getImg",
imgPath: "some/image.png",
canvasID: "someCanvasID"
});
This is a more generic approach (that can be used by any content script with minimum configuration), but it might be simpler to move part of the logic to the content script. E.g.:
Define a function within the content script, that when called with a dataURL creates and draws an image onto a canvas.
Define a function in the background page, that takes an image-path and returns a dataURL (as seen above).
Use chrome.runtime.getBackgroundPage() to get a reference to the background page's window object, call the function to convert an image-path to a dataURL and finally use that dataURL to create an image and draw it onto a canvas.
Try to add your assets to the web_accessible_resources property at the top-level of your manifest file, e.g.
"web_accessible_resources": ["asset/gotcha.png"],
if you have not done so yet.

Categories

Resources