Can't access KML features - openlayers-3

I am loading a KML file locally and I have been able to add it to the map successfully. However, I want to interate over the features and can't seem to get anything to work. My code currently:
var myLayer = new ol.layer.Vector({
source: new ol.source.Vector({
url: '/kml/sample.kml',
format: new ol.format.KML()
})
});
// Iterate over features *NOT WORKING*
myLayer.getSource().forEachFeature(function(e) {
console.log(e);
})
Any pointers on how I can get the forEachFeature to function, or any alternative method, would be great.

The code in your question works fine, except that the features are loaded asynchronously. Most of the time it will first execute forEachFeature, which finds 0 features to loop through and afterwards the features are loaded.
You may find out that a single feature is loaded by listening for the addfeature event of the source and maybe you can make your desired changes there for each feature separately:
var id = 1;
myLayer.getSource().on('addfeature', function (ev_add) {
console.log(ev_add.feature);
ev_add.feature.once('change', function (ev_change) {
console.log(ev_change.target.getId());
});
ev_add.feature.setId(x);
x += 1;
});
If you must wait until all features are loaded, the change event of the layer can help:
myLayer.once('change', function () {
myLayer.getSource().forEachFeature(function (feature) {
console.log(feature);
});
});
Edit: You are right, the addfeature event handler has the event object as parameter. To your question about setting the ID while adding features, I think that this is again a problem of waiting until the changes are done. I made the amendments in the first snippet.

I found a way to get this to work. Not sure if it's the most efficient however:
var featProj = map.getView().getProjection();
var kmlFormat = new ol.format.KML();
var myLayer = new ol.layer.Vector();
var vectorSource = new ol.source.Vector({
loader: function() {
$.ajax( {
url: '/kml/my.kml',
success: function( data ) {
var features = kmlFormat.readFeatures( data, { featureProjection: featProj } );
vectorSource.addFeatures( features );
// iterate over features
vectorSource.forEachFeature( function( feature ) {
//do something
console.log( feature );
});
}
});
},
strategy: ol.loadingstrategy.bbox
});
myLayer.setSource( vectorSource );

Related

PDFTron webviewer - how to save the whole redacted pdf to server using ASP.net MVC Core

I am currently a developing an application in MVC Core that is using a PDFTron webviewer. Is there anyway to save the edited pdf edited with pdftron webviewer to the server?
There is a feature of pdftron that saves annotations to the server, but I need to save the whole pdf with the edits to the server.
WebViewer({
path: '/lib/WebViewer',
initialDoc: '/StaticResource/Music.pdf', fullAPI: !0, enableRedaction: !0
}, document.getElementById('viewer')).then(
function(t) {
samplesSetup(t);
var n = t.docViewer;
n.on('documentLoaded', function() {
document.getElementById('apply-redactions').onclick = function() {
t.showWarningMessage({
title: 'Apply redaction?',
message: 'This action will permanently remove all items selected for redaction. It cannot be undone.',
onConfirm: function () {
alert( );
t.docViewer.getAnnotationManager().applyRedactions()
debugger
var options = {
xfdfString: n.getAnnotationManager().exportAnnotations()
};
var doc = n.getDocument();
const data = doc.getFileData(options);
const arr = new Uint8Array(data);
const blob = new Blob([arr], { type: 'application/pdf' });
const data = new FormData();
data.append('mydoc.pdf', blob, 'mydoc.pdf');
// depending on the server, 'FormData' might not be required and can just send the Blob directly
const req = new XMLHttpRequest();
req.open("POST", '/DocumentRedaction/SaveFileOnServer', true);
req.onload = function (oEvent) {
// Uploaded.
};
req.send(data);
return Promise.resolve();
},
});
};
}),
t.setToolbarGroup('toolbarGroup-Edit'),
t.setToolMode('AnnotationCreateRedaction');
}
);
When i send the request to the Controller i am not getting the file it is coming null
[HttpPost]
public IActionResult SaveFileOnServer(IFormFile file)
{
return Json(new { Result="ok"});
}
Can any one suggest me where i am going wrong
Thanks in adavance
For JavaScript async function, you need to wait for it completes before doing other things. For example, AnnotationManager#applyRedactions() returns a Promise, the same for AnnotationManager#exportAnnotations() and Document#getFileData().
For JS async functions, you can take a look at:
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
So here you may want to use await to wait for the Promise completes.

openlayers 3 featureKey exists in featureChangeKeys

I have an ol.interaction.Select acting on an ol.source.Vector which is within an ol.layer.Vector. I can select and unselect individual countries fine. I am using a dragbox to select multiple countries. If I select anywhere outside of the multiply selected countries, the currently selected get unselected. Excellent!
However, the problem is that if I select a currently selected country within the multiple, I get the AssertionError: Assertion failed: featureKey exists in featureChangeKeys
Here's my Vector layer:
_countrySelectSource = new ol.source.Vector({
url: 'vendor/openlayers/geojson/countries.json',
format: new ol.format.GeoJSON()
});
var countryLayer = new ol.layer.Vector({
title: 'Country Select',
visible: true,
type: 'interactive-layers',
source: _countrySelectSource
});
I add countryLayer to my map, _map.
I then create a _CountrySelect object that allows me to setActive(true|false) on the interactions related to my country selection.
_CountrySelect = {
init : function(){
this.select = new ol.interaction.Select();
_map.addInteraction(this.select);
this.dragbox = new ol.interaction.DragBox({
condition: ol.events.condition.platformModifierKeyOnly
});
_map.addInteraction(this.dragbox);
this.setEvents();
},
setEvents: function(){
var selectedFeatures = this.select.getFeatures();
var infoBox = document.getElementById('info');
var selfDragbox = this.dragbox;
selfDragbox.on('boxend', function() {
// features that intersect the box are added to the collection of
// selected features, and their names are displayed in the "info"
// div
var extent = selfDragbox.getGeometry().getExtent();
_countrySelectSource.forEachFeatureIntersectingExtent(extent, function(feature) {
selectedFeatures.push(feature);
_countryCodes.push(feature.getId());
});
infoBox.innerHTML = _countryCodes.join(', ');
});
// clear selection when drawing a new box and when clicking on the map
selfDragbox.on('boxstart', function() {
selectedFeatures.clear();
infoBox.innerHTML = ' ';
});
_map.on('singleclick', function(event) {
selectedFeatures.clear();
_countryCodes = [];
_map.forEachFeatureAtPixel(event.pixel,function(feature){
selectedFeatures.push(feature);
var id = feature.getId();
var index = _countryCodes.indexOf(id);
if ( index === -1 ) {
_countryCodes.push(feature.getId());
}
});
infoBox.innerHTML = _countryCodes.join(', ');
});
},
setActive: function(active){
this.select.setActive(active);
this.dragbox.setActive(active);
}
};
_CountrySelect.init();
I am not sure if this is an issue with OL3 or my code. Maybe there's an event I'm not handling? Maybe it's the ol.interaction.DragBox (no luck researching on the DragBox). Let me know what further information I can provide.

Tile loading progress for vector sources utilizing tile loading strategy

I am loading features into Vector source utilizing the Tile strategy. I'd like implement kind of progress bar, similar to http://openlayers.org/en/master/examples/tile-load-events.html.
However, unlike VectorTile source, the Vector source doesn't trigger tile loading events which could be used for calculating desired ratio 100*(tilesLoaded/tilesToLoad).
So far I can retrieve the total count of tiles to load, but I am unable to count already loaded tiles. The most promising is a custom loader, but it is not clear to me how to modify it without touching the original OL source code.
var vectorSource = new ol.source.Vector({
loader: ol.featureloader.xhrX(url, format),
strategy: ol.loadingstrategy.tile(tileGrid)
});
// forked method, but the inner 'loadFeaturesXhr' method seems to be private and cannot be used
ol.featureloader.xhrX = function(url, format) {
/*
return ol.featureloader.loadFeaturesXhr(url, format,
function(features, dataProjection) {
this.addFeatures(features);
// when tile loading succeeds
tilesLoaded++;
},
function() {
// when tile loading fails
tilesLoaded++;
});
*/
// just returning the original loader
return ol.featureloader.xhr(url, format);
}
var url = function(extent, resolution) {
tilesToLoad++; // when a new tile is needed, this counter is incremented
var tileCoord = tileGrid.getTileCoordForCoordAndResolution(ol.extent.getCenter(extent), resolution);
return 'tiles/' +
tileCoord[0] + '/' +
tileCoord[1] + '/' +
(Math.pow(2, tileCoord[0]) + tileCoord[2]) + '.json';
}
var format = new ol.format.GeoJSON({
defaultDataProjection: 'EPSG:3857'
});
Any idea how to call loadFeaturesXhr() method from my source?
Instead of forking default OL3 loader I created a custom one. It was not as hard as expected. Now I can freely add counters to proper places (in the code below I actually update the progress component itself):
var vectorSource = new ol.source.Vector({
loader: function(extent, resolution, projection) {
var getUrl = function(extent, resolution) {
progress.addLoading();
...
};
var xhr = new XMLHttpRequest();
xhr.open('GET', getUrl(extent, resolution), true);
xhr.onload = function(event) {
...
progress.addLoaded();
}.bind(this);
xhr.onerror = function(event) {
progress.addLoaded();
}
xhr.send();
},
strategy: ol.loadingstrategy.tile(tileGrid)
});

How to create multiple instances of IIFE Javascript module?

I'm dealing with a huge javascript codebase that I'm trying to reorganize. I'm not really an expert and I just started studying good javascript coding practices. So, one thing I'm trying to do is to divide all the code in modules. In this particular case I'm trying to create a module that would help me to optimize video embeds. I would like to pass the module an id and receive some html code or an image out of it.
I'm not putting the whole code here, but it's enough for the example:
var videoIframe = (function($) {
'use strict';
var id,
setVideoId = function(videoId) {
id = videoId;
console.log(id);
},
getVideoThumbnail = function(videoId) {
setVideoId(videoId);
},
test = function() {
console.log(id)
},
getVideoEmbedCode = function() {
};
return {
test: test,
getVideoThumbnail: getVideoThumbnail
};
})(jQuery);
In another module I assign it to two variables:
var video1 = videoIframe;
var video2 = videoIframe;
video1.getVideoThumbnail(123);
video2.getVideoThumbnail(456);
video1.test();
video2.test();
And, of course, I'm not getting what I expected. After the second getVideoThumbnail call, it always prints 456.
Doing some research I understood that I'm creating a singleton, a single instance, and I'm only changing values inside that instance. I think I need a constructor for my module, but I'm not sure how to create it in combination with the IIFE pattern. And is it the right approach?
And is it the right approach?
No. IIFEs are for things you want to do exactly once.
If you want to do something multiple times, then use a regular function and call it multiple times.
var videoIframe = (function($) {
function videoIframe() {
'use strict';
var id,
setVideoId = function(videoId) {
id = videoId;
console.log(id);
},
getVideoThumbnail = function(videoId) {
setVideoId(videoId);
},
test = function() {
console.log(id)
},
getVideoEmbedCode = function() {
};
return {
test: test,
getVideoThumbnail: getVideoThumbnail
};
}
return videoIframe;
})(jQuery);
var video1 = videoIframe();
var video2 = videoIframe();
video1.getVideoThumbnail(123);
video2.getVideoThumbnail(456);
video1.test();
video2.test();
The problem is that you are initializing object when assigning to videoIframe variable:
var videoIframe = (function($) {
// ...
})(jQuery);
You can try with:
var videoIframe = (function($) {
// ...
});
var video1 = videoIframe(jQuery);
var video2 = videoIframe(jQuery);
Just keep videoIframe as a function that returns your IIFE, instead of reusing the singleton. I kept the IIFE so the id and the inner functions keep being encapsulated and hence, not reachable if they're not in the returned interface object. If you are planning to create alot of these, it might be more efficient to just use a constructor and a prototype, so the inner functions don't get recreated for every instance.
var videoIframe = function() {
'use strict';
return (function() {
var id,
setVideoId = function(videoId) {
id = videoId;
console.log(id);
},
getVideoThumbnail = function(videoId) {
setVideoId(videoId);
},
test = function() {
console.log(id)
},
getVideoEmbedCode = function() {
};
return {
test: test,
getVideoThumbnail: getVideoThumbnail
};
}());
};
I did some minor modification. Hope it will be helpful
var videoIframe = (function($) {
'use strict';
var id;
function _setVideoId(videoId) {
id = videoId;
alert(id);
};
function _getVideoThumbnail(videoId) {
_setVideoId(videoId);
};
function _test(){
console.log(id)
}
function _getVideoEmbedCode() {
};
return {
test: _test,
getVideoThumbnail: _getVideoThumbnail
};
})(jQuery);
Now you can call like this
videoIframe.getVideoThumbnail(123);
videoIframe.getVideoThumbnail(561);
jsfiddle

Calling controller method from JQuery calls occurs twice and also returning error?

Hi guys i have posted a similar post before, but that is for another, now i face a strange and odd issue with my Jquery code. Here i was calling a controller method using Jquery but it is calling twice , so that may cause two entries in my db. Here is what i have written in my JQuery
<script type="text/javascript">
$('#btnSubmit').click(function () {
var instructorUrl = '#Url.Action("ApplyToBecomeInstructor", "InstructorApplication")';
var currentUser = '#Model.CurrentUserId';
var user = [];
var educationList = [];
var experience = $('#Experience').val();
var isWilling = $('#WillingToTravel').is(":checked");
$('#editorRows .editorRow').each(function () {
var education = {
UniversityOrCollege: $(this).find('.university').val(),
AreaOfStudy: $(this).find('.area').val(),
Degree: $(this).find('.degree').val(),
YearReceived: $(this).find('.year').val()
}
educationList.push(education);
});
var applicationFromView = {
EducationalBackgrounds: educationList,
CurrentUserId: currentUser,
Experience: experience,
WillingToTravel: isWilling
}
$.ajax({
type: 'POST',
url: instructorUrl,
dataType: 'JSON',
async: false,
data: JSON.stringify(applicationFromView),
contentType: 'application/json; charset=utf-8',
success: function (data) {
return false;
},
error: function (data) {
alert(xhr.status);
alert(thrownError);
alert(xhr.responseText);
return false;
}
});
});
</script>
and my controller action looks like this
[HttpPost]
public ActionResult ApplyToBecomeInstructor(InstructorApplicationViewModel applicationFromView)
{
Student thisStudent = this.db.Students.Where(o => o.StudentID == applicationFromView.CurrentUserId).FirstOrDefault();
List<PaulSchool.Models.EducationalBackground> educationList = new List<EducationalBackground>();
foreach (var educate in applicationFromView.EducationalBackgrounds)
{
var education = new Models.EducationalBackground
{
YearReceived = educate.YearReceived,
Degree = educate.Degree,
AreaOfStudy = educate.AreaOfStudy,
UniversityOrCollege = educate.UniversityOrCollege
};
educationList.Add(education);
}
var instructorApplication = new InstructorApplication
{
BasicInfoGatheredFromProfile = thisStudent,
Experience = applicationFromView.Experience,
EducationalBackground = new List<Models.EducationalBackground>(),
WillingToTravel = applicationFromView.WillingToTravel
};
instructorApplication.EducationalBackground.AddRange(educationList);
this.db.InstructorApplication.Add(instructorApplication);
this.db.SaveChanges();
return this.Redirect("Index");
}
Error message showing is JSON Parsing error.. but it is confusing to me.
I really wondered why this is happening, can anybody please take a look and help me?
This is what your code does:
$('#btnSubmit').click(function () { // attach a click handler for the button.
...
...
// Look for elements inside the button...
UniversityOrCollege: $(this).find('.university').val(),
Change from click to submit:
$('#formId').submit(function (e) {
...
// Now "this" is the form - not the button.
// Look for elements inside the <form>
UniversityOrCollege: $(this).find('.university').val(),
// Prevent the default form submition
return false // Or: e.preventDefault();
Another tip: use jQuery serialize function.
$('#btnSubmit').click() will fire every time the button is pressed. Often users double click buttons even though it only needs a single click or if you don't give any indication that something is happening they get impatient and click it again. You need some way to determine if the request has been made. There's ways to do this client and server side. The easiest client side way is to disable the button to prevent multiple clicks:
$('#btnSubmit').click(function () {
// Disable the button so it can't be clicked twice accidentally
$('#btnSubmit').attr('disabled', 'disabled');
//...
$.ajax({
//...
complete: function() {
// Make sure we re-enable the button on success or failure so it can be used again
$('#btnSubmit').removeAttr('disabled');
}
});
});

Resources