OpenLayers 3 - SelectInteraction Event Not Firing - openlayers-3

I am trying to fire the select event for a selectInteraction. Here is the code I have so far:
// create and instance of the selectInteraction
var selectInteraction = new ol.interaction.Select( {
layers: myLayers
} );
// add select event handler
// NOT BEING CALLED WHEN FEATURES ARE PUSHED TO SELECTED ARRAY
selectInteraction.on( "select", function ( evt ) {
var selected = evt.selected;
var deselected = evt.deselected;
selected.forEach( function( feature ) {
feature.setStyle( myCustomStyleFunction );
} );
deselected.forEach( function( feature ) {
feature.setStyle( null );
} );
}, selectInteraction );
// add the interaction to the map
myMap.getInteractions().extend( [ selectInteraction ] );
// function called with feature to be selected
function programmaticallySelectFeature( feature ) {
// get the selectInteraction for the map
myMap.getInteractions().forEach( function ( interaction ) {
if ( interaction instanceof ol.interaction.Select ) {
selectInteraction = interaction;
}
});
// push the feature to the selectInteraction
selectInteraction.getFeatures().push( feature );
}
I understand that the select event is not firing when features are pushed to the selected array. Otherwise it works as expected. So how can get this to work? Can I listen for another event perhaps?

you can listen to the events click or singleclick on the map, and push the returned feature in the selected features:
map.on('click', function(evt){
var feature = map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) {
return feature;
});
if(feature){
selectInteraction.getFeatures().push(feature);
}
});
this of course assuming you want to select features with a click

Related

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.

Can't access KML features

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 );

Arrow from HeaderCell in Angular ui-grid

I need to know whether or not it is possible to remove the down chevron arrow from the headercell of the Angular ui-grid (as seen below):
Because the Sort Ascending and Sort Descending in the dropdown selection does not behave the same as the sort when clicking on column headings in the grid. Clicking on column headings automatically overrides any previous sorting, whereas selecting Sort Ascending or Descending from the dropdown requires that the user selects "Remove Sort" before selecting another column. My QA Team has asked me to "remove" it as they fear it would cause a user to believe that there is something wrong with the sorting feature if the tried to select another Sort Asc/Desc without first clicking Remove Sort. If the arrow cannot be removed, is it at least possible to remove Sort Ascending and Descending from the selection without preventing sorting using the column headings?
In order to remove the Sort Ascending, Sort Descending and Remove Sort from the dropdown menu, I commented out the following in the ui-grid.js file:
//{
// title: i18nService.getSafeText('sort.ascending'),
// icon: 'ui-grid-icon-sort-alt-up',
// action: function($event) {
// $event.stopPropagation();
// $scope.sortColumn($event, uiGridConstants.ASC);
// },
// shown: function () {
// return service.sortable( $scope );
// },
// active: function() {
// return service.isActiveSort( $scope, uiGridConstants.ASC);
// }
//},
//{
// title: i18nService.getSafeText('sort.descending'),
// icon: 'ui-grid-icon-sort-alt-down',
// action: function($event) {
// $event.stopPropagation();
// $scope.sortColumn($event, uiGridConstants.DESC);
// },
// shown: function() {
// return service.sortable( $scope );
// },
// active: function() {
// return service.isActiveSort( $scope, uiGridConstants.DESC);
// }
//},
//{
// title: i18nService.getSafeText('sort.remove'),
// icon: 'ui-grid-icon-cancel',
// action: function ($event) {
// $event.stopPropagation();
// $scope.unsortColumn();
// },
// shown: function() {
// return service.sortable( $scope ) &&
// typeof($scope.col) !== 'undefined' && (typeof($scope.col.sort) !== 'undefined' &&
// typeof($scope.col.sort.direction) !== 'undefined') && $scope.col.sort.direction !== null &&
// !service.suppressRemoveSort( $scope );
// }
//},
Now, the only thing that shows in the dropdown menu is "Hide Column". This solved my issue.
Hope this helps someone else!

JqueryUI Detect when a group of items are on their droppable target areas

I have 4 drag and drop items, they thay all drag and dropp onto their targets, but should then fire off a 'welldone' event and the part is not working. I've had some help but can;t get this working, what am I doing wrong?
// insert code to be run when the symbol is created here
yepnope({nope:['scripts/jquery-ui-1.10.3.custom.min.js','scripts/jquery.ui.touch-punch.min.js'], complete: init})
// Initial state: not dropped
sym.setVariable("dropped", "false");
function init(){
//Use Jquery code for draggable and droppable
//Drag it
sym.$('scrambled_egg').draggable({opacity:.5, revert:'invalid'});
//Drop it on the target
sym.$('scrambled_target').droppable({
accept:sym.$("scrambled_egg"),
drop: function () {
sym.getSymbol("scrambled_egg").play();
// Store that you dropped it
sym.setVariable("dropped", "true");
// Call a function to check if all the symbols are dropped
// and fire event "done"
checkIfAllDropped();
}
}
);
//Snap back to default state
sym.$('scrambled_default').droppable({
accept:sym.$("scrambled_target"),
drop: function () {
// Back to not dropped state
sym.setVariable("dropped", "false");
}
}
);
//End code chunk
//Use Jquery code for draggable and droppable
//Drag it
sym.$('fried_egg').draggable({opacity:.5, revert:'invalid'});
//Drop it on the target
sym.$('fried_target').droppable({
accept:sym.$("fried_egg"),
drop: function () {
sym.getSymbol("fried_egg").play();
// Store that you dropped it
sym.setVariable("dropped", "true");
// Call a function to check if all the symbols are dropped
// and fire event "done"
checkIfAllDropped();
}
}
);
//Snap back to default state
sym.$('fried_default').droppable({
accept:sym.$("fried_target"),
drop: function () {
// Back to not dropped state
sym.setVariable("dropped", "false");
}
}
);
//End code chunk
//Use Jquery code for draggable and droppable
//Drag it
sym.$('poached_egg').draggable({opacity:.5, revert:'invalid'});
//Drop it on the target
sym.$('poached_target').droppable({
accept:sym.$("poached_egg"),
drop: function () {
sym.getSymbol("poached_egg").play();
// Store that you dropped it
sym.setVariable("dropped", "true");
// Call a function to check if all the symbols are dropped
// and fire event "done"
checkIfAllDropped();
}
}
);
//Snap back to default state
sym.$('poached_default').droppable({
accept:sym.$("poached_target"),
drop: function () {
// Back to not dropped state
sym.setVariable("dropped", "false");
}
}
);
//End code chunk
//Use Jquery code for draggable and droppable
//Drag it
sym.$('boiled_egg').draggable({opacity:.5, revert:'invalid'});
//Drop it on the target
sym.$('boiled_target').droppable({
accept:sym.$("boiled_egg"),
drop: function () {
sym.getSymbol("boiled_egg").play();
// Store that you dropped it
sym.setVariable("dropped", "true");
// Call a function to check if all the symbols are dropped
// and fire event "done"
checkIfAllDropped();
}
}
);
//Snap back to default state
sym.$('boiled_default').droppable({
accept:sym.$("boiled_target"),
drop: function () {
// Back to not dropped state
sym.setVariable("dropped", "false");
}
}
);
//End code chunk
}
checkIfAllDropped = function(){
var stage = AdobeEdge.getComposition("How_do_you_eat_yours").getStage();
var sym1 = stage.getSymbol("scrambled_target");
var sym2 = stage.getSymbol("fried_target");
var sym2 = stage.getSymbol("boiled_target");
var sym2 = stage.getSymbol("poached_target");
if(sym1.getVariable("dropped") === "true" &&
sym2.getVariable("dropped") === "true" &&
sym3.getVariable("dropped") === "true" &&
sym4.getVariable("dropped") === "true"){
// Fire event done!
stage.play("welldone");
}
};
I would do it this way: every time you drop an element you store this event somewhere and you check if all the elements are dropped.
For example:
// Initial state: not dropped
sym.setVariable("dropped", "false");
sym.$('scrambled_target').droppable({
accept:sym.$("scrambled_egg"),
drop: function () {
sym.getSymbol("scrambled_egg").play();
// Store that you dropped it
sym.setVariable("dropped", "true");
// Call a function to check if all the symbols are dropped
// and fire event "done"
checkIfAllDropped();
}
}
);
sym.$('scrambled_default').droppable({
accept:sym.$("scrambled_egg"),
drop: function () {
// Back to not dropped state
// EDIT HERE! Dind't get that sym was your stage.
// AND EDIT EVERYWHERE ELSE.
sym.getSymbol("scrambled_egg").setVariable("dropped", "false");
}
}
);
The checkIfAllDropped() function will look something like this:
checkIfAllDropped = function(){
var stage = AdobeEdge.getComposition("COMPOSITION_CLASS_NAME").getStage();
var sym1 = stage.getSymbol("sym1");
var sym2 = stage.getSymbol("sym2");
if(sym1.getVariable("dropped") === "true" &&
sym2.getVariable("dropped") === "true"){
// Fire event done!
stage.play("done");
}
};
Be careful on your checkIfAllDropped function you are assigning var sym2 multiple times.
checkIfAllDropped = function(){
var stage = AdobeEdge.getComposition("How_do_you_eat_yours").getStage();
var sym1 = stage.getSymbol("scrambled_target");
// Here...
var sym2 = stage.getSymbol("fried_target");
// Here...
var sym2 = stage.getSymbol("boiled_target");
// Here...
var sym2 = stage.getSymbol("poached_target");
if(sym1.getVariable("dropped") === "true" &&
sym2.getVariable("dropped") === "true" &&
sym3.getVariable("dropped") === "true" &&
sym4.getVariable("dropped") === "true"){
// Fire event done!
stage.play("welldone");
}
};
Please debug your code before posting it again!

jQuery Mobile tap and taphold sensitivity issue in iOS

In my jQuery Mobile app, I want to use tap and taphold events. I tried using the standard approach of binding event handlers to these events but in case of taphold event, the tap event was always firing so I used the following approach which I found on stackoverflow here:
jQuery calling click event after taphold event
$("#list li").live('vmousedown vmouseup', function (event)
{
if (event.type == 'vmousedown')
{
tapTime = new Date().getTime();
}
else
{
//event.type == 'vmouseup'
//here you can check how long the `tap` was to determine what do do
duration = (new Date().getTime() - tapTime);
//The tap code
if(duration >250 && duration <750)
{
}
//The taphold code
else if (duration >=750) {
}
Now, on an iPhone with iOS 5, I am having the problem that the tap event is being fired and an item is selected when I scroll down a list. I tried to increase the duration for tap event but it seems to have no effect in iOS. Any suggestions?
[Tried and Tested]
I checked jQuery Mobile's implementation. They are firing the 'tap' event after 'taphold' every time on 'vmouseup'.
Workaround would be not to fire the 'tap' event if the 'taphold' has been fired. Create a custom event or modify the source as per you need as follows:
$.event.special.tap = {
tapholdThreshold: 750,
setup: function() {
var thisObject = this,
$this = $( thisObject );
$this.bind( "vmousedown", function( event ) {
if ( event.which && event.which !== 1 ) {
return false;
}
var origTarget = event.target,
origEvent = event.originalEvent,
/****************Modified Here**************************/
tapfired = false,
timer;
function clearTapTimer() {
clearTimeout( timer );
}
function clearTapHandlers() {
clearTapTimer();
$this.unbind( "vclick", clickHandler )
.unbind( "vmouseup", clearTapTimer );
$( document ).unbind( "vmousecancel", clearTapHandlers );
}
function clickHandler( event ) {
clearTapHandlers();
// ONLY trigger a 'tap' event if the start target is
// the same as the stop target.
/****************Modified Here**************************/
//if ( origTarget === event.target) {
if ( origTarget === event.target && !tapfired) {
triggerCustomEvent( thisObject, "tap", event );
}
}
$this.bind( "vmouseup", clearTapTimer )
.bind( "vclick", clickHandler );
$( document ).bind( "vmousecancel", clearTapHandlers );
timer = setTimeout( function() {
tapfired = true;/****************Modified Here**************************/
triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
}, $.event.special.tap.tapholdThreshold );
});
}
};

Resources