I have a layer with overlapping features (e.g. bounding boxes). In OL2 the select control seemed to select the expected feature (e.g. the feature with less surface area). In OL3 this doesn't seem to be the case. While I could get all features at a specific pixel, I would prefer for the select control to return all features that intersect with the click. Any way to do this?
You can set the multi member of ol.interaction.Select to true (it allows to choose all features at the coordinate you have clicked) and add an event to choose what feature do you want to select among all the overlapping features:
var select = new ol.interaction.Select({
multi: true
});
var fnHandler = function (e) {
e.selected; // array of selected features
e.target; // select interaction
var feature = e.selected.filter(function (feature) {
// do some filtering to choose what feature do you want
})[0];
e.target.getFeatures().clear(); // unselect all features
e.target.getFeatures().push(feature); // select the feature you filtered
};
select.on('select', fnHandler);
To get all of the features in a layer that intersect with the mouse click I do something like this:
map.on("click", function(event) {
var coordinate = event.coordinate;
var features = myVectorLayer.getSource().getFeaturesAtCoordinate(coordinate);
// Do something with the features that were clicked here...
});
Related
I am new to OL.
I need to implement a logic that should happen if I pressed on a specific item in a specific layer.
I have to state that I'm not writing a project from scratch, and I actually inherited a very basic but complex system.
The system gets the layers from MapGuide 2.5.
This is how the map is initiated:
var map = new ol.Map({
loadTilesWhileInteracting:true,
layers: this.layers,
target: this._element[0],
controls: controls,
interactions: interactions,
view: view
});
view.fit(that.extent, map.getSize());
I tried adding a select interaction - it didn't work (my promise was never called).
var select_interaction = new ol.interaction.Select();
select_interaction.getFeatures().on("add", function (e) {
var feature = e.element; //the feature selected
});
map.addInteraction(select_interaction);
I tried:
map.on('click', function (evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel,
function (feature, layer) {
debugger;
this.log("fff")
});
});
In this case, the promise works but I get no features.
EDIT:
I also tried:
var feature = map.forEachLayerAtPixel(evt.pixel,
function (feature, layer) {..}
but I get the exception:
uncaught security error: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data. ol.js:341
How can I do this?
Thanks,
Ido
I was getting the same error while I was trying to loop through layers at pixel via forEachLayerAtPixel method.
Thanks to #kagelos comment, I managed to solve the problem simply by filtering the layers and looping through only Vector layers.
Layer types are roughly separated in two categories in OL. Image layers (tiles etc.) and vector layers. You have to inspect the contents of this.layers that you pass as an argument in the constructor of the map and see what type of layers it contains. You can only interact directly with vector type layers.
Here is the final code:
Map.forEachLayerAtPixel( cursorPosition, function ( _layer ) {
// you will get the vector layer here
}, this, function ( _layer ) {
if ( _layer instanceof ol.layer.Vector ) {
return true;
}
});
I am creating an application which utilises a map created and managed by the OpenLayers 3 library. I want to be able to switch which layer is visible using the zoom level (i.e. zoom out to get an overview of countries, zoom in to get an overview of cities). There are three categories of layers (3 different zoom levels), and within each category there are 3 colours which the pins I am using could be (which are all separate layers as well) so in total there are 9 layers.
What I want is to develop the ability to filter which layers are displayed, which means showing/hiding the existing layers depending on which zoom level we are at.
There is some code to demonstrate how the map is generated and how one type of layer is generated but if there is more detail required please let me know. I don't believe there will be an issue with this, however.
function setUpMap(vectorLayers, $scope){
var view = new ol.View({
center: ol.proj.fromLonLat([2.808981, 46.609599]),
zoom: 4
});
map = new ol.Map({
target: 'map',
layers: vectorLayers,
overlays: [overlay],
view: view
});
view.on("change:resolution", function(e){
var oldValue = e.oldValue;
var newValue = e.target.get(e.key);
if (newValue > 35000){
if (oldValue < 35000)
//This is where I will show group 1
} else if (newValue > 10000){
if (oldValue < 10000 || oldValue > 35000)
//This is where I will show group 2
} else {
if (oldValue > 10000)
//This is where I will show group 3
}
});
addClickEventsToMapItems($scope);
}
I tried something like this and got no success:
function showLayer(whichLayer){
vectorLayers[1].setVisibility(false);
vectorLayers[2].setVisibility(false);
vectorLayers[3].setVisibility(false);
vectorLayers[whichLayer].setVisibility(true);
}
I am open to suggestions. Please let me know! :)
You can listen to the resolution:change event on your ol.Map instance:
map.getView().on('change:resolution', function (e) {
if (map.getView().getZoom() > 0) {
vector.setVisible(true);
}
if (map.getView().getZoom() > 1) {
vector.setVisible(false);
}
});
Example on Plunker: http://plnkr.co/edit/szSCMh6raZfHi9s6vzQX?p=preview
It's also worth to take a look at the minResolution and maxResolution options of ol.layer which can switch automaticly for you. But it works by using the view's resolution, not the zoomfactor:
http://openlayers.org/en/v3.2.1/examples/min-max-resolution.html
why dont you use minResolution/maxResolution parameters during vector layer initialasation, check the api here
If you dont know the resolution but you know only the scale, use the following function to get the resolution out of the scale
function getResolutionFromScale(scale){
var dpi = 25.4 / 0.28;
var units = map.getView().getProjection().getUnits();
var mpu = ol.proj.METERS_PER_UNIT[units];
var res = scale / (mpu * 39.37 * dpi);
return res;
}
I made a function to get the closer feature to the one clicked. I use Openlayers 3.9.0 and the getClosestFeatureToCoordinate method.
var select = new ol.interaction.Select();//simple click interaction
map.addInteraction(select);//add it to the map
select.on('select', function(e) {
//get the extent of the first selected feature
var aa = e.selected[0].getGeometry().getExtent();
//in case of line or polygon get the center of that extent
var oo = ol.extent.getCenter(aa);
//use it to get the name of the closest feature
console.log((sourceVector.getClosestFeatureToCoordinate(oo)).get("mylayer_name")) ;
});
But in a case like the following
if I click the "u" Polygon (bottom down) I get "u" instead of , say, "e"
if I click any point I get its name , instead of the closest feature's name. I click "testpoint9" and I get "testpoint9" instead of "u" or "e". I click "h" and I get "h" instead of "p" or "k".
So maybe has to do with points, so I changed the select.on event function to
select.on('select', function(e) {
var closestType = e.selected[0].getGeometry().getType();
var oo;
if (closestType === 'Point'){
oo = e.selected[0].getGeometry().getCoordinates();
}
else{
var aa = e.selected[0].getGeometry().getExtent();
oo = ol.extent.getCenter(aa);
}
console.log("---------------------------------------------------");
console.log("Name: "+sourceVector.getClosestFeatureToCoordinate(oo).get('mylayer_name'));
})
and still nothing. So, how I fix this?
Thanks
When You click inside the "u" polygon - the distance to it is 0.
When you click on something - that thing will always be the closest (to itself).
What you can do there, is removing the clicked element from the layer, run the algorithm (without clicked point) and put the point back on the layer.
If You are afraid that point could be invisible for too long (but it shouldn't), place it in another layer for the time of algorithm.
select.on('select', function(e) {
var clicked = e.selected[0];
sourceVector.removeFeature(clicked);
var closest = sourceVector.getClosestFeatureToCoordinate(e.coordinate);
sourceVector.addFeature(clicked);
var closestType = closest.getType();
if (closestType === 'Point'){
oo = e.selected[0].getGeometry().getCoordinates();
}
else{
var aa = e.selected[0].getGeometry().getExtent();
oo = ol.extent.getCenter(aa);
}
})
I create a text box where I can put the coordinates X,Y then I move the pin to the location and find which roads are there.
The pin move, but forEachFeatureAtPixel only return one feature instead of four. Also the feature I got doesn't have NOMBRE property
I'm using Openlayer 3 and my WMS server map.
As you can see in MapInfo (picture right side) I got all 4 features. Horizontal 2x "Calle 6" and Vertical 2x "Calle 1 Norte", and have NOMBRE property.
I'm using the same x, y from mapinfo where all 4 feature instersect.
new info: My layer is ol.layer.Tile instead of ol.layer.Vector, maybe that is the problem (checking)
wmsLyr09 = new ol.layer.Tile({
source: wmsSource
});
// move the pin to new position
geometry.translate(deltaX, deltaY);
var coordinate = geometry.getCoordinates();
var pixel = map.getPixelFromCoordinate(coordinate);
console.log('pixel: ' + pixel);
var allFeaturesAtPixel = [];
map.forEachFeatureAtPixel(pixel, function (feature) {
allFeaturesAtPixel.push(feature);
});
// feature[len]:1
console.log("feature[len]:" + allFeaturesAtPixel.length);
//feature[Name]: undefined
feature= allFeaturesAtPixel[0];
console.log("feature[Name]: " + feature.get('NOMBRE'));
In your map you probably have two layers, the WMS tile layer and the vector layer for the pin. So, OpenLayers doesn't know about the road features. If you are calling forEachFeatureAtPixel you are getting the only feature OpenLayers knows about: the pin.
What you want to do is make a WFS GetFeature request to get the features at the current pin position. Take a look at this example: http://openlayers.org/en/master/examples/getfeatureinfo-tile.html
I'm working primarily in the macro language.
For my macro, I am asking a user to select a few elements from an image. One is an ellipse selection and the other polygonal. I have these two ROIs in the ROI Manager and I would like to keep them.
When I do further analysis on the image to isolate different tissues I need to use analyze particles to add more ROIs to the ROI Manager, but when I add them, it erases the user selected ROIs.
Is there a way to append the ROIs to the list or do I need to reorder my macro?
run("Select None"); //Selecting nothing just in case
setTool(1); //Elliptical tool
while (selectionType != 1) {
message= "Elliptical Selection Required\n Please select the Peduncle Attachment Zone";
waitForUser(message);
}
roiManager("Add");
// User selection of Seed Cavity
// hexagonal (polygon) selection
run("Select None"); //Selecting nothing just in case
setTool(2); //Elliptical tool
while (selectionType != 2) {
message= "Polygonal Selection Required\n Please select the Seed Cavity";
waitForUser(message);
}
roiManager("Add");
run("Select None"); //clear current selection
//threshold here
//analyze particles
run("8-bit");
setThreshold(0, 128);
setOption("BlackBackground", false);
run("Convert to Mask");
correctLUT(); //checks if inverted and corrects
run("Analyze Particles...", "size=55000-Infinity circularity=0.65-1.00 show=Masks display exclude add");
function correctLUT() {
invertedLUT = is("Inverting LUT");
if (invertedLUT == 1) {
run("Invert LUT");
run("Invert");
}
}