Openlayers 2. z-axis and select control - javascript

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.

Related

Vector Tile Points on top of Polygons can't be clicked on

I have made my own vector tile .pbf data, consisting of polygons, lines and points.
The source of .pbf data are from a grouplayer of shapefiles in Geoserver, where each shapefile has been ordered so that points are at the top:
Using OpenLayers, I've styled the vector tiles and followed the example for "Vector Tile Info". The difference is mine is toggled by single click.
The problem is that I can't click on any point, even as a sprite image, if it is completely within a polygon.
It's possible to click on lines that are completely within a polygon.
Underlying polygons are selected as feature[0] when attempting to select a point.
What might be the cause of this render order? That a point is only a single pixel?
What might be the remedy?
I'm very new to JavaScript and OpenLayers so I'm hoping it's something naive.
I need to be able to prioritize the points when they're surrounded by a polygon.
The order of feature returned by map.getFeaturesAtPixel() seems to be random and not related to render order. Either check all the features returned, not just entry [0], or sort the array so Points/MultiPoints are lower in the sort order than LineStrings/MultiLineStrings with Polygons last
function rank(feature) {
var type = feature.getType();
if (type.indexOf('Point') > -1) {
return 1;
} else if (type.indexOf('LineString') > -1) {
return 2;
} else {
return 3;
}
}
map.getFeaturesAtPixel(event.pixel).sort(function(a, b) {
return rank(a) - rank(b);
});

Detect when user reaches maxBounds using Leaflet

I am using leaflet to show an interactive map to our users.
We want to let them browse through a limited area, and inform them they have to subscribe in case they want to see something too far away (using a pop up or equivalent).
So far I have seen that Leaflet supports a maxBounds option.
This is a good start that lets me prevent users to see larger areas.
Now I would like to be able to detect a maxBounds 'event' to show the user a pop up.
I have been looking into the Leaflet source code, but couldn't find an obvious way to do it.
so far I have found that the maxBounds option is fed into the setView method.
This method itself uses the _limitCenter method to define the center.
This goes a few levels deeper, down to the _getBoundsOffset method that finally uses the bounds.
_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
var projectedMaxBounds = toBounds(
this.project(maxBounds.getNorthEast(), zoom),
this.project(maxBounds.getSouthWest(), zoom)
),
minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
dx = this._rebound(minOffset.x, -maxOffset.x),
dy = this._rebound(minOffset.y, -maxOffset.y);
return new Point(dx, dy);
},
The closest I could find so far would be to hook into the moveend event and check whether the center is out of my bounds manually.
However, it seems like this would be redundant with what leaflet is already doing.
Is there a better to leverage leaflet to achieve this?
Thanks
Just check if your defined bounds contain the map bounds. As long as the map bounds are inside the defined bounds, this will do nothing:
var myBounds = L.latLngBounds(...)
map.on('move moveend zoomend', function(){
if (!myBounds.contains(map.getBounds())) {
// Display popup or whatever
}
});
it seems like this would be redundant with what leaflet is already doing.
Don't worry about that. The overhead is negligible for this use case.

OpenLayers 3 hover to highlight only one specified object per time

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.

Delete a vertex of polygon google maps API v3

So there are many posts on how to delete the vertex of a google maps polygon. None of them have worked for me - this may be due to the outdated API's they reference. I am trying to limit the number of points a user can create on a polygon. If they create more than a certain number of points I would like to either programmatically trigger the 'undo' functionality built into google maps or have some way of deleting the last vertex created.
When I create the initial polygon for a user to work with I add this event listener in:
google.maps.event.addListener(myPolygon.getPath(), 'insert_at', function(event){
var numPoints = myPolygon.getPath().length;
if(numPoints == 5){
myPolygon.getPath().removeAt(4);
}
});
This ends up removing a point from the polygon (NOT necessarily the last point created). Sometimes it causes the following error:
Uncaught TypeError: Cannot read property 'b' of undefined.
I have attempted using a 'click' listener on the polygon, but the click event does not fire when a user creates a new vertex. It only fires when the actual polygon shaped is clicked.
I guess your are using an path with auto-completition(the first LatLng is not equal to the last LatLng)
There seems to be a bug when you try to remove the last LatLng from an autocompleted path.
This bug doesn't prevent you from removing the point, the error occurs later(I guess when the API updates the UI for the editable polygon).
You may simply use a try/catch-statement to bypass this error.
Related to the position of the removed point: a newly created point will not be inserted at the end of the path, it will be inserted between the points where it's placed.
When you insert a point between point1(index 0) and point2(index 1) the newly created point will have the index 1.
You may use the first argument of the callback to retrieve this index.
Summary:
google.maps.event.addListener(myPolygon.getPath(), 'insert_at', function(index){
if(this.getLength() > 4){
try{
this.removeAt(index);
}catch(e){}
}
});

Openlayers - coordinates saving after drag cause point go to 0,0

I have a map with a dragable point and after drag, I update latitude and longitude fields in the form. But when I do that like this:
drag = new OpenLayers.Control.DragFeature(vectors, {
autoActivate: true,
onComplete: function() {
$('#place_latitude').val(point.transform(mapp, wgs84).y);
return $('#place_longitude').val(point.transform(mapp, wgs84).x);
}
});
After attempt to make second drag (from one point to another) point goes to 0,0. Without onComplete everything is ok.
You are transforming the point two times... transform method modifies point itself, it doesn't create new object.
You may use point.clone() instead of point.

Categories

Resources