Esri Take Map Scrrenshots using Javascript - javascript

I am trying to generate a pdf report including current map screenshot of ESRI map using JavaScript. Here is my code.
var getCurrentMapScreenShot = function (success, error) {
esriLoader.Config.defaults.io.proxyUrl = myAppSettingsModel.SettingsModel.MapSettings.AGSProxyURL;
esriLoader.Config.defaults.io.alwaysUseProxy = true;
var printTask = new esriLoader.PrintTask("myexportUrl");
var template = new esriLoader.PrintTemplate();
template.exportOptions = {
width: 600,
height: 600,
dpi: 96
};
template.format = "image/png";
template.layout = "MAP_ONLY",
template.preserveScale = true;
template.layoutOptions = {
legendLayers: [], // empty array means no legend
scalebarUnit: "Miles"
};
var params = new esriLoader.PrintParameters();
params.map = map;
params.template = template;
printTask.execute(params, success, error);
}
This function will give you an event that contains a url , Then I am passing this url to get map base64 data.
Here is the function calling.
map.GetCurrentMapScreenShot(function (event) {
var mapScreenShotURL = event.url;
Factory.GetBase64ForImgUrl(mapScreenShotURL,
function (mapImageBase64Encoded) {});
and here is the function that converts url to base64image
function getBase64ForImgUrl(url, callback, outputFormat) {
console.log("#################### Summary Report Image " + url);
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL(outputFormat || 'image/png');
callback.call(this, dataURL);
// Clean up
canvas = null;
};
img.src = url;
}
I am getting base64 image data of the map , But the problem is that, I am getting blurred image of the map with no feature layers and no icons..And also map contains some junk texts.
Thanks

I resolved the issue almost, Now I am getting the map image that contains map Icons and layer graphics, But unfortunately still getting some junk texts.
I have changed the code as follows.
template.format = "JPG";
template.layout = "A4 Landscape",
template.preserveScale = false;
template.layoutOptions = {
"legendLayers": [], // empty array means no legend
"scalebarUnit": "Miles"
}

Related

Sending image manipulated via JS in AJAX POST request

I'm a server-side dev learning the ropes of vanilla JS. I need to clear my concepts regarding sending an Ajax POST request for an image object I'm creating in JS - this question is about that.
Imagine a web app where users upload photos for others to see. At the point of each image's upload, I use vanilla JS to confirm the image's mime-type (via interpreting magic numbers), and then resize the image for optimization purposes.
After resizing, I do:
var canvas = document.createElement('canvas');
canvas.width = resized_width;
canvas.height = resized_height;
var ctx = canvas.getContext("2d");
ctx.drawImage(source_img, 0, 0, resized_width, resized_height);
var resized_img = new Image();
resized_img.src = canvas.toDataURL("image/jpeg",0.7);
return resized_img;
The image object returned has to be sent to the backend via an Ajax request. Something like:
function overwrite_default_submit(e) {
e.preventDefault();
var form = new FormData();
form.append("myfile", resized_img, img_name);
var xhr = new XMLHttpRequest();
xhr.open('POST', e.target.action);
// xhr.send(form); // uncomment to really send the request
}
However, the image object returned after resizing is essentially an HTML element like so <img src="data:image/jpeg;base64>. Whereas the object expected in the FormData object ought to be a File object, e.g. something like: File { name: "example.jpg", lastModified: 1500117303000, lastModifiedDate: Date 2017-07-15T11:15:03.000Z, webkitRelativePath: "", size: 115711, type: "image/jpeg" }.
So what do I do to fix this issue? Would prefer to learn the most efficient way of doing things here.
Btw, I've seen an example on SO of using the JS FILE object, but I'd prefer a more cross-browser method, given File garnered support from Safari, Opera Mobile and built-in Android browsers relatively recently.
Moreover, only want pure JS solutions since I'm using this as an exercise to learn the ropes. JQuery is on my radar, but for later.
The rest of my code is as follows (only included JPEG processing for brevity):
var max_img_width = 400;
var wranges = [max_img_width, Math.round(0.8*max_img_width), Math.round(0.6*max_img_width),Math.round(0.4*max_img_width),Math.round(0.2*max_img_width)];
// grab the file from the input and process it
function process_user_file(e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = process_image;
reader.readAsArrayBuffer(file.slice(0,25));
}
// checking file type programmatically (via magic numbers), getting dimensions and returning a compressed image
function process_image(e) {
var img_width;
var img_height;
var view = new Uint8Array(e.target.result);
var arr = view.subarray(0, 4);
var header = "";
for(var i = 0; i < arr.length; i++) {
header += arr[i].toString(16);
}
switch (header) {
case "ffd8ffe0":
case "ffd8ffe1":
case "ffd8ffe2":
case "ffd8ffe3":
case "ffd8ffe8":
// magic numbers represent type = "image/jpeg";
// use the 'slow' method to get the dimensions of the media
img_file = browse_image_btn.files[0];
var fr = new FileReader();
fr.onload = function(){
var dataURL = fr.result;
var img = new Image();
img.onload = function() {
img_width = this.width;
img_height = this.height;
resized_img = resize_and_compress(this, img_width, img_height, 80);
}
img.src = dataURL;
};
fr.readAsDataURL(img_file);
to_send = browse_image_btn.files[0];
load_rest = true;
subform.disabled = false;
break;
default:
// type = "unknown"; // Or one can use the blob.type as fallback
load_rest = false;
subform.disabled = true;
browse_image_btn.value = "";
to_send = null;
break;
}
}
// resizing (& compressing) image
function resize_and_compress(source_img, img_width, img_height, quality){
var new_width;
switch (true) {
case img_width < wranges[4]:
new_width = wranges[4];
break;
case img_width < wranges[3]:
new_width = wranges[4];
break;
case img_width < wranges[2]:
new_width = wranges[3];
break;
case img_width < wranges[1]:
new_width = wranges[2];
break;
case img_width < wranges[0]:
new_width = wranges[1];
break;
default:
new_width = wranges[0];
break;
}
var wpercent = (new_width/img_width);
var new_height = Math.round(img_height*wpercent);
var canvas = document.createElement('canvas');
canvas.width = new_width;
canvas.height = new_height;
var ctx = canvas.getContext("2d");
ctx.drawImage(source_img, 0, 0, new_width, new_height);
console.log(ctx);
var resized_img = new Image();
resized_img.src = canvas.toDataURL("image/jpeg",quality/100);
return resized_img;
}
Update: I'm employing the following:
// converting image data uri to a blob object
function dataURItoBlob(dataURI,mime_type) {
var byteString = atob(dataURI.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); }
return new Blob([ab], { type: mime_type });
}
Where the dataURI parameter is canvas.toDataURL(mime_type,quality/100)
You should call the canvas.toBlob() to get the binary instead of using a base64 string.
it's async so you would have to add a callback to it.
canvas.toBlob(function(blob) {
resized_img.onload = function() {
// no longer need to read the blob so it's revoked
URL.revokeObjectURL(this.url);
};
// Preview the image using createObjectURL
resized_img.src = URL.createObjectURL(blob);
// Attach the blob to the FormData
var form = new FormData();
form.append("myfile", blob, img_name);
}, "image/jpeg", 0.7);
See to this SO post: How to get base64 encoded data from html image
I think you need to call 'canvas.toDataURL()' to get the actual base64 stream of the image.
var image = canvas.toDataURL();
Then upload it with a Form: Upload a base64 encoded image using FormData?
var data = new FormData();
data.append("image_data", image);
Untested, but this should be about it.

Loading image to canvas after retrieving from camera roll (cordova)

I'm fetching a file from the iOS camera roll, which returns a uri in this form:
file:///var/mobile/Containers/Data/Application/90849385-6FC7-4C4E-8220-7585312C3DFB/tmp/filename.jpg
I receive this uri from this function:
function openFilePicker(selection) {
var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
var options = setOptions(srcType);
var func = createNewFileEntry;
navigator.camera.getPicture(function cameraSuccess(imageUri) {
loadToCanvas(imageUri);
}, function cameraError(error) {
console.debug("Unable to obtain picture: " + error, "app");
}, options);
}
and pass it to this function:
function loadToCanvas(path) {
console.log(path);
var ctx = document.getElementById('image');
ctx = ctx.getContext('2d');
var hero = new Image();
hero.onload = function () {
ctx.drawImage(hero, 0, 0);
};
hero.src = path;
}
The image is not showing in the canvas but there are no errors in the evothings console. What am I missing? Surely if cordova has the means of returning this uri to me then I should have access to the file, yeah? Thanks!

javascript fabricjs filter do not return filtered canvas to url

export function filter(url) {
var c = document.createElement('canvas')
c.id = "canvas_greyscale"
var canvas = new fabric.Canvas('canvas_greyscale')
fabric.Image.fromURL(url, function(oImg) {
c.height = oImg.height
c.width = oImg.width
oImg.filters.push(new fabric.Image.filters.Grayscale())
oImg.applyFilters(canvas.renderAll.bind(canvas))
canvas.add(oImg)
var img = canvas.toDataURL('image/png')
console.log(img)
return img
}, {crossOrigin: "Anonymous"})
}
Here my canvas is rendered with grayscale filter. It does not have issue but when I try to convert canvas to url it is giving me the canvas without filter.
I dont know whats wrong in here..
What am I doing wrong. I want to convert the canvas that have filter to url
var img = canvas.toDataURL('image/png') gives me image without filter
Need help
applyFilters is asynchronous (that's why you pass a renderAll callback in it).
You need to call toDataURL in its callback otherwise you're exporting the canvas before the filter is applied.
Here is a rough adaptation of your code :
function filter(url, callback) {
var c = document.createElement('canvas');
c.id = "canvas_greyscale";
var canvas = new fabric.Canvas('canvas_greyscale');
// the applyFilters' callback
var onend = function() {
canvas.renderAll();
var img = canvas.toDataURL('image/png');
callback(img);
}
fabric.Image.fromURL(url, function(oImg) {
canvas.setDimensions({width:oImg.width, height:oImg.height});
oImg.filters.push(new fabric.Image.filters.Grayscale())
// here we pass the export function
oImg.applyFilters(onend)
canvas.add(oImg)
}, {
crossOrigin: "Anonymous"
})
}
var url = 'https://upload.wikimedia.org/wikipedia/commons/5/5b/Supernumerary_rainbow_03_contrast.jpg'
filter(url, function(dataURI) {
output.src = dataURI;
original.src = url
})
img{ width: 50%}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.js"></script>
<img id="output"><br>
original Image: © Andrew Dunn CC-By-SA 2.0 <br>
<img id="original">

Saving multiple SVGs to canvas with text then getting dataURL

I have built an angularJS application, in this application SVG files represent garments that a user chooses. I have a download button which (currently) saves the first SVG as a PNG into a database and I use a view to display this "preview".
The directive I created looks like this:
.directive('kdExport', function () {
return {
restrict: 'A',
scope: {
target: '#kdExport',
team: '='
},
controller: 'ExportImageController',
link: function (scope, element, attrs, controller) {
console.log(scope.team);
// Bind to the onclick event of our button
element.bind('click', function (e) {
// Prevent the default action
e.preventDefault();
// Generate the image
controller.generateImage(scope.target, scope.team, function (preview) {
// Create our url
var url = '/kits/preview/' + preview.id;
// Open a new window
window.open(url, '_blank');
});
});
}
};
})
and the controller looks like this:
.controller('ExportImageController', ['PreviewService', function (service) {
var self = this;
// Function to remove the hidden layers of an SVG document
var removeHidden = function (element) {
// Get the element children
var children = element.children(),
i = children.length;
// If we have any children
if (children.length) {
// For each child
for (i; i >= 0; i--) {
// Get our child
var child = angular.element(children[i - 1]);
// Remove hidden from the child's children
removeHidden(child);
// Finally, if this child has the class "hidden"
if (child.hasClass("hidden")) {
// Remove the child
child.remove();
}
}
}
};
// Public function to generate the image
self.generateImage = function (element, team, onSuccess) {
// Get our SVG
var target = document.getElementById(element),
container = target.getElementsByClassName('svg-document')[0],
clone = container.cloneNode(true);
// Remove hidden layers
removeHidden(angular.element(clone));
// Create our data
var data = clone.innerHTML,
svg = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
// Get our context
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
// Create our image
var DOMURL = window.URL || window.webkitURL || window,
url = DOMURL.createObjectURL(svg),
img = new Image();
// When the image has loaded
img.onload = function () {
canvas.width = 1000;
canvas.height = 500;
// Draw our image using the context
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 1000, 500);
DOMURL.revokeObjectURL(url);
// Get our URL as a base64 string
var dataURL = canvas.toDataURL("image/png");
// Create our model
var model = {
teamName: team.name,
sport: team.sport,
data: dataURL
};
// Create our preview
service.create(model).then(function (response) {
// Invoke our success callback
onSuccess(response);
});
}
// Set the URL of the image
img.src = url;
};
}])
This works fine for a single SVG document, but now the client has asked me to do this for multiple SVGs with a title under each one and they want it all in one PNG.
I have not done a lot of work with canvasing, so I am not sure if this can be done.
Does anyone know how I might achieve this?
Ok, so I figured this out myself using promises.
Basically I created a method called drawImage that allowed me to draw an image for each SVG.
To make sure that all images were drawn before I invoke toDataURL I made the function return a promise and once the image loaded I resolved that promise.
Then I just used a $q.all to get the dataURL and save the data to my database.
The methods looked like this:
// Private function for drawing our images
var drawImage = function (canvas, ctx, clone) {
// Defer our promise
var deferred = $q.defer();
// Create our data
var data = clone.innerHTML,
svg = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
// Create our image
var DOMURL = window.URL || window.webkitURL || window,
url = DOMURL.createObjectURL(svg),
img = new Image();
// When the image has loaded
img.onload = function () {
// Get our location
getNextLocation(canvas.width, canvas.height, img);
// Draw our image using the context (Only draws half the image because I don't want to show the back)
ctx.drawImage(img, 0, 0, img.width / 2, img.height, location.x, location.y, location.width, location.height);
DOMURL.revokeObjectURL(url);
// Resolve our promise
deferred.resolve();
}
// Set the URL of the image
img.src = url;
// Return our promise
return deferred.promise;
};
// Public function to generate the image
self.generateImage = function (element, team, onSuccess) {
// Get our SVG
var target = document.getElementById('totals'),
containers = angular.element(target.getElementsByClassName('svg-document'));
// Get our context
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
// Set our canvas height and width
canvas.width = 2000;
canvas.height = calculateCanvasHeight(containers.length);
// Create our array of promises
var promises = [];
// For each container
for (var i = 0; i < containers.length; i++) {
// Get our container
var container = containers[i],
clone = container.cloneNode(true);
// Remove hidden layers
removeHidden(angular.element(clone));
// Add our promise to the array
promises.push(drawImage(canvas, ctx, clone));
}
// When all promises have resolve
$q.all(promises).then(function () {
// Get our URL as a base64 string
var dataURL = canvas.toDataURL("image/png");
// Create our model
var model = {
teamName: team.name,
sport: team.sport,
data: dataURL
};
// Create our preview
self.create(model).then(function (response) {
// Invoke our success callback
onSuccess(response);
});
})
};
Obviously there is missing code here, but this code answers my issue, the rest just makes my service work :)

WinJS barcode reader issues(Image not loading in canvas)

Am working on a winjs based barcode reader application. Initially I will capture the image using camera capture API and will pass that file object to a canvas element and read its barcode using ZXing library. But the image passed to the canvas is not getting rendered completely as follows.
Following is my html code
<body>
<p>Decoding test for static images</p>
<canvas id="canvasDecode" height="200" width="200"></canvas>
<h3 id="result"></h3>
<p>Put some content here and leave the text box</p>
<input id="input" class="win-textarea" onchange="generate_barcode()">
<h3 id="content"></h3>
<canvas id="canvasEncode" height="200" width="200"></canvas>
<img class="imageHolder" id="capturedPhoto" alt="image holder" />
</body>
following is my javascript code
(function () {
"use strict";
WinJS.Binding.optimizeBindingReferences = true;
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
// TODO: This application has been newly launched. Initialize
// your application here.
var dialog = new Windows.Media.Capture.CameraCaptureUI();
var aspectRatio = { width: 1, height: 1 };
dialog.photoSettings.croppedAspectRatio = aspectRatio;
dialog.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo).then(function (file) {
if (file) {
// draw the image
var canvas = document.getElementById('canvasDecode')
var ctx = canvas.getContext('2d');
var img = new Image;
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
}
img.src = URL.createObjectURL(file);
// open a stream from the image
return file.openAsync(Windows.Storage.FileAccessMode.readWrite);
}
})
.then(function (stream) {
if (stream) {
// create a decoder from the image stream
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(stream);
}
})
.done(function (decoder) {
if (decoder) {
// get the raw pixel data from the decoder
decoder.getPixelDataAsync().then(function (pixelDataProvider) {
var rawPixels = pixelDataProvider.detachPixelData();
var pixels, format; // Assign these in the below switch block.
switch (decoder.bitmapPixelFormat) {
case Windows.Graphics.Imaging.BitmapPixelFormat.rgba16:
// Allocate a typed array with the raw pixel data
var pixelBufferView_U8 = new Uint8Array(rawPixels);
// Uint16Array provides a typed view into the raw 8 bit pixel data.
pixels = new Uint16Array(pixelBufferView_U8.buffer);
if (decoder.bitmapAlphaMode == Windows.Graphics.Imaging.BitmapAlphaMode.straight)
format = ZXing.BitmapFormat.rgba32;
else
format = ZXing.BitmapFormat.rgb32;
break;
case Windows.Graphics.Imaging.BitmapPixelFormat.rgba8:
// For 8 bit pixel formats, just use the returned pixel array.
pixels = rawPixels;
if (decoder.bitmapAlphaMode == Windows.Graphics.Imaging.BitmapAlphaMode.straight)
format = ZXing.BitmapFormat.rgba32;
else
format = ZXing.BitmapFormat.rgb32;
break;
case Windows.Graphics.Imaging.BitmapPixelFormat.bgra8:
// For 8 bit pixel formats, just use the returned pixel array.
pixels = rawPixels;
if (decoder.bitmapAlphaMode == Windows.Graphics.Imaging.BitmapAlphaMode.straight)
format = ZXing.BitmapFormat.bgra32;
else
format = ZXing.BitmapFormat.bgr32;
break;
}
// create a barcode reader
var reader = new ZXing.BarcodeReader();
reader.onresultpointfound = function (resultPoint) {
// do something with the resultpoint location
}
// try to decode the raw pixel data
var result = reader.decode(pixels, decoder.pixelWidth, decoder.pixelHeight, format);
// show the result
if (result) {
document.getElementById("result").innerText = result.text;
}
else {
document.getElementById("result").innerText = "no barcode found";
}
});
}
});
} else {
// TODO: This application has been reactivated from suspension.
// Restore application state here.
}
args.setPromise(WinJS.UI.processAll());
}
};
app.oncheckpoint = function (args) {
// TODO: This application is about to be suspended. Save any state
// that needs to persist across suspensions here. You might use the
// WinJS.Application.sessionState object, which is automatically
// saved and restored across suspension. If you need to complete an
// asynchronous operation before your application is suspended, call
// args.setPromise().
};
app.start();
})();
function generate_barcode() {
// get the content which the user puts into the textbox
var content = document.getElementById("input").value;
// create the barcode writer and set some options
var writer = new ZXing.BarcodeWriter();
writer.options = new ZXing.Common.EncodingOptions();
writer.options.height = 200;
writer.options.width = 200;
writer.format = ZXing.BarcodeFormat.qr_CODE;
// encode the content to a byte array with 4 byte per pixel as BGRA
var imagePixelData = writer.write(content);
// draw the pixel data to the canvas
var ctx = document.getElementById('canvasEncode').getContext('2d');
var imageData = ctx.createImageData(imagePixelData.width, imagePixelData.heigth);
var pixel = imagePixelData.pixel
for (var index = 0; index < pixel.length; index++) {
imageData.data[index] = pixel[index];
}
ctx.putImageData(imageData, 0, 0);
}
The same code worked well when I was using the file picker API. Let me knew where I went wrong.
I think that you're running into some problems with asynchronicity here. I applaud your use of chained calls to then(), but there's a hidden problem - assignment to img.src begins an asynchronous operation while the image is loaded. Your code continues on BEFORE the img.onload event has been raised, and so the closure which img.onload reaches into for the img variable (the pointer to the file URL) changes before the image has fully loaded.
Here's some code that worked for me.
// Inside handler for app.activated ...
var dialog = new Windows.Media.Capture.CameraCaptureUI();
var aspectRatio = { width: 1, height: 1 };
dialog.photoSettings.croppedAspectRatio = aspectRatio;
dialog.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo)
.then(function (file) {
// draw the image
var canvas = document.getElementById('canvasDecode')
var ctx = canvas.getContext('2d');
var img = new Image;
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
// open a stream from the image
decodePic(file);
}
img.onerror = function (err) {
WinJS.log && WinJS.log("Error loading image");
}
img.src = URL.createObjectURL(file);
});
And then I moved the file decoding / barcode reading stuff to a new function.
function decodePic(file) {
file.openAsync(Windows.Storage.FileAccessMode.readWrite)
.then(function (stream) {
if (stream) {
// create a decoder from the image stream
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(stream);
}
})
.done(function (decoder) {
if (decoder) {
// get the raw pixel data from the decoder
decoder.getPixelDataAsync().then(function (pixelDataProvider) {
// YOUR BARCODE READING CODE HERE.
});
}
});
}
I hope this helps!

Categories

Resources