Original issue:
OpenLayers 3 (tested also newest 3.14.1) should have some sense in the way it selects features that overlap each other. When multi=false it selects only one feature, but picks it quite randomly. When you hover over certain features from different directions, different feature is selected.
I have tried to tangle this by setting z-index to features to tell Openlayers the desired order. Indexing is set to order point > line > polygon but still I am not getting point selected first on hover.
Z-Index is applied to layer with:
rendererOptions: { zIndexing: true }
on layer and
zIndex: x
in style for different feature types.
What happened first:
Z-indexing did not work so I was not able to tangle the situation for select. I kept multi=true and filtered results manually from list.
I had a function getSelectedRemovableFeature(event.selected) that took event.selected and returned only one feature out of it.
So, my selection had a code like follows:
var selectionInteraction = new ol.interaction.Select({
layers: [layersModule.getTargetLayer()],
condition: ol.events.condition.click,
multi: true
});
and more..
selectionInteraction.on('select', function(event) {
var selectedFeature = null;
selectedFeature = getSelectedRemovableFeature(event.selected);
if(selectedFeature){
.. some logic..
layersModule.getTargetLayer().getSource().removeFeature(selectedFeature);
}
});
Then I got stucked:
I had
var hoverInteraction = new ol.interaction.Select({
layers: [layersModule.getTargetLayer()],
condition: ol.events.condition.pointerMove,
multi: true
});
..and..
hoverInteraction.on('select', function(event) {
var selectedFeature = null;
selectedFeature = getSelectedRemovableFeature(event.selected);
// here I didn't know what to do..
}
});
I had there a logic like this:
selectControl = new OpenLayers.Control.SelectFeature(vectorLayer);
...
map.addControls([selectControl]);
selectControl.activate();
// unselect any specific feature...
selectControl.unselect(vectorLayer.features[0]);
(https://gis.stackexchange.com/questions/41017/how-can-i-unselect-a-feature-in-openlayers)
.. but when it was there, no selections were removed, it worked like same as without this part.
Back to the origins:
Now I realized, that tangling with the hoverSelection data is of no use, because new multi=true type event is thrown there before I could calculate how to select one only and show that. I believe that is the case, because nothing changes even I manually filter the results and remove features.
In short finally:
How to determine explicit order where the hover / select selection goes?
Hit detection of features on a map works in opposite rendering order. By default, points are rendered last, so they get hit detected (and selected) first. When no zIndex is set on any style, the rendering order is polygons, lines, points, texts.
When you set zIndex on an ol.style.Style, you override this fixed rendering order, allowing you to e.g. render polygons on top of points.
Within these four groups (polygons, lines, points, texts), you can control the rendering order by setting a renderOrder function on your ol.layer.Vector. That is an array sort function called with two features:
new ol.layer.Vector({
renderOrder: function(a, b) {
if (a.get('importance') < b.get('importance')) {
return -1;
} else if (b.get('importance') > a.get('importance')) {
return 1;
}
return 0;
}
});
The above assumes that your features have a numeric 'importance' property. Now when you hit detect (select) features of the same geometry type (e.g. points), those with a higher 'importance' value will be selected first, because they are rendered last.
Also note that there are no rendererOptions like you state in your question.
I noticed that from some reason the draw styles included heavily z-indexing and what made this problem to occur was the following:
Reason:
From some historical reason selected style was forced on top by zIndex: 100. Normal selection had zIndex: 1.
That caused a problem: when you had point and line on top of each other and you select line, it's zIndex rose up to 100. That broke the natural highlight order, because 100 is far more than 1. Thus natural order, which #ahocevar pointed out, gets broken and highlighting becomes dependent of direction where you come near to the feature on map.
Solution:
I put selected and default style both to zIndex: 1 and everything seemed to work ok event with that.
I will need to check still the reason of selected style on zIndex so high, but if there is no reason to continue its usage, this thing solves the problem.
Side note:
Answer is a community wiki, because half of solution is from comment of another user.
Related
I managed to select every thing I want in Illustrator with a ExtendScript Toolkit javascript code: lots of things (text, path, symbols, ...) in several layers. Now I want to resize them the same way, then move them.
I know how to apply a transformation to one object by code, but I want to avoid looping on each element because it would be very very long and transformation would be applied from the anchor point of each element, so my drawings wouldn't be cohesive.
So, I am looking for a way to do like in Illustrator UI: right click > transform > scale. Is there any UI command like this I could access from code (javascript)?
There are at least three ways to do this:
Record AI Actions that perform required transformations, and then play these Actions (by DoScript) from your script
Group selected objects and apply required transformations to the group as #Dane proposed. You need to backup the Layer object property to allow objects to be restored in original layers, as shown in VBA example below:
For i = Index_Lo To Index_Hi
Call Layers_Backup.Add(Item:=SelectedItems(i).Layer, Key:=Format(i))
Call SelectedItems(i).Move(Temp_Group, AiElementPlacement.aiPlaceAtEnd)
Next i
Call Temp_Group.Resize(scaleX:=120, scaleY:=120, changeLineWidths:=120)
For i = Index_Lo To Index_Hi
Call SelectedItems(i).Move(Layers_Backup(Format(i)), AiElementPlacement.aiPlaceAtEnd)
Next i
Call Windows API functions (like PostMessage (..., WM_COMMAND, ..., ...), SendDlgItemMessage, FindWindowEx etc) to show, fill and execute required AI transformations dialog boxes
IMHO, item #1 is the easiest to implement while item#2 is the most reliable
So I dont know if you can get away with not looping in some form or other. that said, if you dont mind putting your selection in to a group, it might be faster to loop through your selection adding to a group which might be faster than looping and through the selection and scale and moving each element. With the group object you can then do groupObject.scale() and .translate().
Here is a snippet i took from a script of mine.
#target "illustrator"
var aiApp = app.activeDocument;
var aSelection = aiApp.selection;
var aGroup = app.activeDocument.groupItems.add();
for (i=0; i < aSelection.length; i++){
aSelection[i].move( aGroup, ElementPlacement.INSIDE);
aSelection[i].move( aGroup, ElementPlacement.PLACEATEND);
}
//moves up 700 points and scales by 200
aGroup.translate(0,700)
aGroup.resize(200, 200, true , true, true, true, 200)
I'm working on a graph in d3, that performs multitude of animations on different events. For one of those, I had to bring selected element to the front, which I did using following bit:
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
Thing is, after all the code I needed it in the front is performed, I want to put that element back exactly where I took it from. So, If an element had an index of 1 (out of 7 elements available), then I moved it to front (which changed the index to 6), now I want to set the index back to 1.
I tried putting it at the bottom of index array already (asigning it index 0), but it does not suit my needs.
Also, I'm willing to get rid of moveToFront() prototype, if there's any better way to accomplish what I want, that's just the best way I could find for now.
Below written code highlights a county when a mouse is moved on to a particular county. I want to highlight the entire division(set of counties) when the mouse is moved on to any county of that particular division..
map = new Map("mapDiv", {
basemap: "streets",
center: [-96.921387,36.084621],
zoom: 8,
slider: false
});
var oklahomaCounties = new FeatureLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/3",
{
//mode: FeatureLayer.MODE_SNAPSHOT,
outFields: ["NAME"]
});
oklahomaCounties.setDefinitionExpression("STATE_NAME = 'Oklahoma'");
I found a sample I guess it's seems similar to your need.
http://developers.arcgis.com/javascript/sandbox/sandbox.html?sample=fl_hover
Hope this will help you :)
If you need working solution please share exact code where your are facing issue.
Edited:
Based on below discussion below are the steps to achieve it.
We have two way to achieve it.
First Approach:
Onhover; I fetch all the division records from given layer and add it to the map but this will cause you performance issue like after hovering you have to wait utill you get all the records.
Second Approach
Initially on load of app get all records from above layer, this will be one time activity; process the output data/features and create collection in such a manner so that key will be division name and features push to its specific division. once you are ready with collection then simply on hover which ever feature you are getting first find from which group/division its belonging and highlight all the feature from that division on the map.
I would recommend to go with this because it will not fetch every time data; only first load time will increased a little bit.
Let me know if any clarifications required.
By using the simple array app.selection[x], you can apply a transformation to any object in the selection, independently. But how do I apply a transformation to the entire selection together?
For example: inside InDesign, I can select two side-by-side objects and flip them horizontally, causing them to switch places and flip.
Inside a script, I can target each object in the selection, but they will not switch places; they will remain in the same place and flip.
for ( var x = 0; x < app.selection.length; x++ ){
app.selection[x].absoluteFlip = Flip.HORIZONTAL;
}
I could possibly group the selection, apply a transformation, then ungroup when finished, but this seems like unnecessary bulk that could slow down the code. I can easily do it manually inside InDesign, so it should follow that there's some way to access app.selection as a single object instead of an array. Does such an object exist?
Not really a solution, but it's worth noting that I don't think absoluteFlip is the action being performed, but a state indicating if the item has ben flipped. It's writable so you can set the state, but I think what's happening when using the menu to flip is flipItem: http://jongware.mit.edu/idcs6js/pc_PageItem.html#flipItem,
in which you can set "around" coordinates.
Now getting the origin of the selection box isn't straightforward for some reason (or maybe it is but I don't know how), but you can either use first object coordinates to set the origin so you can flip it around different coordinates depending on order of selection. Or you can sort the array to find left most coordinates (or whichever is needed), like this:
var selection_array = app.selection;
selection_array.sort(function(a, b){return a.visibleBounds[1]-b.visibleBounds[1]})
var flip_origin = [selection_array[0].visibleBounds[1],selection_array[0].visibleBounds[0]]
for(i=0;i<app.selection.length;i++){
app.selection[i].flipItem(Flip.HORIZONTAL, flip_origin);
}
Not sure it's easier or faster than grouping and ungrouping though.
Consider resize. It has a "individual/global" parameter :
void resize (in: varies, from: varies, by: ResizeMethods, values: Array of varies[, resizeIndividually: bool=true][, consideringRulerUnits: bool=false])
Resize the page item.
I've got a couple vector layers one has polygons, one has lines. We have a need to add lines to the line layer that attach to the polygons (database procedure requires the polygons IDs, which are stored in attributes on the polygons)
So I have a drawFeature control on the lineLayer, and a selectFeature (which stores off the ID on hover rather than just selecting) on the polygonLayer. It actually works just fine except the z axis of the linelayer while added is lower, so it shows the new line being drawn under the polygon. Would rather have the line show over the polygon. I know it's because when the selectFeature control is active it's setting the z-index of the polygon layer higher than the lineLayer.
I manually set the z-index of the linelayer higher than the polygon layer using lineLayer.setZIndex(800) or whatever, and that certainly makes the new line draw over the polygons, but then the selectFeature events don't trigger.
I've considered several solutions including adding the drawFeature to my polygon layer and then moving the feature to the line layer when done, but it's still rendering under the polygons, I even played with the graphicZIndex on the "temporary" style for my stylemap on the polygon layer. to no avail (i did set the renderOptions zindexing to true on the polygon layer)
I may be approaching this from the wrong angle, so I'm open to suggestions. If there was a function available on the vector layer something like getFeatureByPosition(position), I could grab the position on sketchStarted, and sketchEnded events and query that, but so far I've been unable to find anything like that.
I'm not on my dev box at the moment in case anyone is wondering why no code. Wanted to post this from work, but the base network is having issues displaying the logon page (due to ssl I think)
So my solution required a few things. First I needed to upgrade from OL 2.10 to the latest 2.13.1. Mostly this was due to needing a new event that was added to 2.11 (I think or maybe 2.12) the event is "featureover" which can be caught at the map level and therefore will trigger on all layers, so I'm not fighting with Z-Index of the select. I removed the select control from the polygon layer as it was not needed.
var featureOverHandler = function(event){
if (event.feature.layer.id == polygonLayer.id) {
selectedPolygonId = event.feature.attributes.POLYGON_ID;
console.log("Selected Polygon Id: " + selectedPolygonId);
map.events.unregister('featureover',map,featureOverHandler);
map.events.register('featureout',map,featureOutHandler);
}
};
var featureOutHandler = function (event) {
if (event.feature.layer.id == polygonLayer.id) {
selectedPolygonId = 0;
console.log("Cleared Selected Polygon ID ");
map.events.unregister('featureout', map, featureOutHandler);
map.events.register('featureover', map, featureOverHandler);
}
};
These will catch the polygon that is currently hovered over. But then I add events to catch where the line starts and ends. Since as of 2.11, they changed the way the "sketchstarted" event works, you can non longer use that to capture which polygon the pointer was over when the first point was added. I used the point callback on the vector layer.
var vDrawOptions = {
callbacks: {
"point": function (p) {
if (p.parent.components.length == 2) {
console.log("First Point added");
startingPolygonId = selectedPolygonId;
}
}
}
}
vectorAddControl = new OpenLayers.Control.DrawFeature(vectorLayer, OpenLayers.Handler.Path,vDrawOptions);
however, the "skecthcomplete" can still be used to capture the ending point (and thus polygon)
function vSketchComplete(evt) {
endingPolygonId = selectedPolygonId;
};
vectorLayer.events.register('sketchcomplete', vectorLayer, vSketchComplete);
Hopefully this will help someone else, in a similar situation.