OpenLayers ModifyFeature not saving new vertices - javascript

I'm just getting started with OpenLayers, and have hit a small snag - when I create a LineString and then try to modify it, I can move the existing vertices and drag the virtual vertices to create new ones. When I continue to add to the line though, only the changes to the existing vertices are saved - new vertices are discarded. Am I missing something? You can see an example of what I'm talking about here:
http://dev.darrenhall.info/temp/open-layers/modify-feature/
Click to add points, and use the dots to edit, then click to continue adding to see what I mean. Any help would be appreciated! Thanks!
Darren

After a quick look, your code looks more complex than it should be.
You manually push point into an array of point manually on click, and generate a linestring with those points.
You don’t listen to any change done with virtual vertices. I don’t get why, in your addWayPoint function, you don’t get the geometry of the feature from the layer rather than your array of point.
Maybe that would be a good start to use the real feature geometry and avoid using your route.waypoints.

In the end I decided not to use modifyFeature, and instead went for using vectors as handles and manually handling the dragging and line modification. You can see my workaround here:
http://dev.darrenhall.info/temp/open-layers/draw-route
The guys at Ordnance Survey cam up with a (rather simple) fix for my code though that repopulates the array from the vertices after modification:
function addWayPoint(e) {
var position = osMap.getLonLatFromViewPortPx(e.xy);
if(route.waypoints.length>1) {
layers.lines.layer.removeFeatures([layers.lines.feature]);
}
/* vvvvvvvvvvv start */
/* Get the potentially modified feature */
if (modifyFeature.feature) {
route.waypoints = [];
var vertices = modifyFeature.feature.geometry.getVertices();
for (i = 0; i < vertices.length; i++) {
//console.log(vertices[i]);
route.waypoints.push(vertices[i]);
}
}
/* ^^^^^^^^^^^ end */
route.waypoints.push(new OpenLayers.Geometry.Point(position.lon, position.lat));
var string = new OpenLayers.Geometry.LineString(route.waypoints);
layers.lines.feature = new OpenLayers.Feature.Vector(string, null, styles.pink);
layers.lines.feature.attributes['id']=1;
layers.lines.layer.addFeatures([layers.lines.feature]);
for (i = 0; i < layers.lines.layer.features.length; i++) {
if (layers.lines.layer.features[i].attributes.id == 1) {
modifyFeature.selectFeature(layers.lines.layer.features[i]);
}
}
}

Related

In Bing Maps v8 can I get a pushpin object from a layer in a loop?

In Bing Maps v7 I was able to add pushpins to an entityCollection and then loop through that collection later in the code to set options or whatever. Now, I am having trouble getting pins from the v8 layers.
Here is what I used to do in v7 after I had already added the pin to the entityCollection:
for (var i = 0; i < entityCollection.getLength() ; i++) {
var pin = entityCollection.get(i);
pin.setOptions({ visible: true });
}
I have changed the object entityCollection to a layer for v8 and I am also looping through the layer while i < entityCollection.data.length
Now, in Bing Maps v8, I'm having trouble getting the pin object from the layer that I have already added it to. The code above throws an error on the setOptions line and I have also tried getting the pin with:
entityCollection.data[i]
instead of
entityCollection.get(i)
But that doesn't work either. I'm afraid my question is too generic, because I can't find anything that actually answers my question. I have a work around, but that causes failures later when I want to hide all the pins with certain attributes. Thanks in advance!
Bing Maps v8 has done away with the entityCollection - though they say it's still sort of supported, you obviously don't want to be using deprecated things any more.
Anywhere you have an entityCollection, replace it with a Layer (Microsoft.Maps.Layer). Layers expose the getPrimitives() method which will provide you with an array of the contents.
var map = new Microsoft.Maps.Map(..., ...);
var layer = new Microsoft.Maps.Layer();
// Add layer data...
layer.add(new Microsoft.Maps.Pushpin(...));
// Add layer to map
map.layers.insert(layer);
// Then you can iterate
var layerItems = layer.getPrimitives();
var len = layerItems.length;
for(var i = 0; i < len; i++){
var pin = layerItems[i];
// Do something with your pin
pin.setOptions({visible: false});
}
Note that if you're doing mass updates to the entire contents of the layer, such as showing or hiding every pin in the layer, you can do that directly on the layer. This will save you (the browser) a chunk of work setting each pin individually.
layer.setVisible(true);
Yes, in V8 layers have a getPrimitives function which returns an array containing all the shapes. You can then loop through these shapes like you would any other array.

Best way to share out many points on a few features?

I have 5000+ LatLng points, and for each of them I would like to find out which feature (region) they belong to. The features come from a kmz layer by Philippe Ivaldi, converted to GeoJSON.
Currently, I am doing this with turfjs in a double for loop. As expected, the calculation freezes the browser for ten minutes, which ain't very convenient.
Here's my code :
function countCeaByLayer(geoJsonLayer){
jQuery.getJSON('http://localhost/server/retrieveData.php', function(data){
var turfPoints = [];
for(var i = 0; i < data.length; i++){
turfPoints.push(turf.point([data[i].longitudeWGS84, data[i].latitudeWGS84]));
}
var features = geoJsonLayer.toGeoJSON().features;
for(var i = 0; i < features.length; i++){
var turfPointsNew = [];
for(var j = 0; j < turfPoints.length; j++){
var isInside = turf.inside(turfPoints[j], features[i]);
if(!isInside) turfPointsNew.push(turfPoints[j]);
}
turfPoints = turfPointsNew;
}
console.log("done");
});
}
What can I do to avoid freezing the browser?
Make it async?
Do the calculation with node and turfjs on a server?
Or deploy leafletjs on a server with node and leaflet-headless?
...or should I just deal with it?
Thanks!
To optimize your code, you should do something like this.
Loop over the points.
For each point, when you iterate over polygons to know if the point is inside one of them, first get the polygon Bounds and see if the point is within the bounds.
If not, you can skip going further and go to the next polygons.
If it's within the bounds, go for a plain check if it is inside the polygon itself.
If it's the case, break the loop iterating over polygons and switch to the next point.
For example, it could be:
points.forEach(function(point) {
polygons.some(function(polygon) {
if (polygon.getBounds().contains(point)) { // or other method if you are not playing with Leaflet features
if (turf.isInside(polygon, point) { // for example, not sure this method actually exists but you get the concept
// point is within the polygon, do tuff
return true; // break the some loop
}
}
});
});
I've myself developped something that exactly does the same thing also based on turf, I run it on the client side (and my loops are made with .some, not classical for loop, so it could even go further in terms of performance) and I never experienced freeze.
From my point of view, 5000 points is peanut for browser to handle, but if your polygons are really complex (dozen of hundreds of thousands of vertices), this can slow down the process of course.
Br,
Vincent
If Stranded Kid's answer is overkill for you,
geoJsonLayer.eachLayer(function(layer){
var within = turf.within(turf.featureCollection(turfPoints),turf.featureCollection([layer.toGeoJSON()]));
console.dir(within);
});
And make sure your coordinates are floats and not strings, because that's what caused the slowdown for me.

Experiencing something odd when using THREE.Raycaster for collision detection (r68)

I've been using the THREE.Raycaster successfully to test collisions for many things in my game engine so far, it's great and it works well.
However, recently I've run into something quite peculiar which I cannot seem to figure out. From my point of view, my logic and code are sound but the expected result is not correct.
Perhaps I'm just missing something obvious so I thought I'd ask for some help.
I am casting rays out from the center of the top of a group of meshes, one by one, in a circular arc. The meshes are all children of a parent Object3D and the goal is to test collisions between the origin mesh and other meshes which are also children of the parent. To test my rays, I am using the THREE.ArrowHelper.
Here's an image of the result of my code - http://imgur.com/ipzYUsa
In this image, the ArrowHelper objects are positioned (origin:direction) exactly how I want them. But yeah, there's something wrong with this picture, the code that is produces this is:
var degree = Math.PI / 16,
tiles = this.tilesContainer.children,
tilesNum = tiles.length,
raycaster = new THREE.Raycaster(),
rayDirections, rayDirectionsNum, rayOrigin, rayDirection, collisions,
tile, i, j, k;
for (i = 0; i < tilesNum; i++) {
tile = tiles[i];
rayOrigin = new THREE.Vector3(
tile.position.x,
tile.geometry.boundingBox.max.y,
tile.position.z
);
rayDirections = [];
for (j = 0; j < Math.PI * 2; j += degree) {
rayDirections.push(new THREE.Vector3(Math.sin(j), 0, Math.cos(j)).normalize());
}
rayDirectionsNum = rayDirections.length;
for (k = 0; k < rayDirectionsNum; k++) {
rayDirection = rayDirections[k];
raycaster.set(rayOrigin, rayDirection);
collisions = raycaster.intersectObjects(tiles);
this.testRay(rayOrigin, rayDirection, collisions);
}
}
The testRay method looks like this:
testRay: function (origin, direction, collisions) {
var arrowHelper = new THREE.ArrowHelper(
direction,
origin,
1,
(collisions.length === 0) ? 0xFF0000 : 0x0000FF
);
this.scene.add(arrowHelper);
}
Now, obviously, something is off about this image. The rays that collide with other meshes should be blue, while those that do not collide should be red.
It's clear from this image that something is totally out of whack, and when I inspect the collisions, I get some really off results. For a lot of those rays which appear blue in the image, I'm getting a huge number of collisions, something like 30 collisions for a single ray sometimes, but nothing for the others even when they are right next to other tiles.
I just can't figure out what it might be. How can it be that so many rays that should be blue are red? And how can rays from tiles at the edge of the level have blue collisions to tiles that do not exist?
Really scratching my head (read: bashing my head repeatedly) over this one, any help would be super appreciated!
The solution was actually outside this code and not, at least I don't believe, related to the outdated r68 build.
When making the tile meshes, I needed to set three properties on them
tileMesh.matrixAutoUpdate = false;
tileMesh.updateMatrix();
tileMesh.updateMatrixWorld(); // this is new
I was doing the first two, just not the last one. Why this is necessary, I do not know, it seems a little odd to me but this is what fixed my problem. I had an AxisHelper in the scene, if you look at the original image, you'll notice that all the ArrowHelper objects that are blue are actually pointing towards the AxisHelper. This is really weird because the AxisHelper was added to the scene, not to tilesContainer. Adding the ArrowHelper objects to tilesContainer did not help.
The process to render the scene had the raycaster code run before the AxisHelper was added to the scene and before the initial render happened. The problem was also fixed if I moved the raycaster code call after the AxisHelper was added, but this was a hacky solution.
So the true fix was to add .updateMatrixWorld() to the tiles. The result now looks like this http://imgur.com/8LewqxL, which is correct (the ArrowHelper objects have been shortened in length so they don't overlap).
Big thanks to Manthrax for his help on this one.
I think you make some local vs global space error. I don't see so fast where exactly you go wrong, but all your position and direction calculations seem to be in the local system of the tilesContainer. Are you consistent in your local vs global coordinate system handling?
For example you add your arrowHelper to the scene instead of to the tilesContainer. It could be that the tilesContainer has some rotation set and because of this the arrows are pointing in another direction then you expected.
What happens for example if you add the arrows to the tilesContainer instead?

Dynamically adding and removing segments from a track in OpenLayers 3

I want to display a track in real-time with OpenLayers 3 which disolves at the end, just like a snails trail.
Only appending new coordinates to a LineString is easy. See this example. But removing coordinates from the end of the line does not seem to be supported by the API.
How should I go about that? Is extending the LineString class the only option? Or should I use a separate feature for each line segment?
Update:
I used this code with ol-debug.js. But get/setFlatCoordinates is not exported in the compiled version.
var flatCoordinates = geometry.getFlatCoordinates(); // not exported
if (flatCoordinates && flatCoordinates.length > 100) {
// remove first coordinate elements from array
flatCoordinates.splice(0, geometry.getStride());
// call push with coordinate elements as arguments
Array.prototype.push.apply(flatCoordinates, coordinate);
// update coordinates calling change()
geometry.setFlatCoordinates(geometry.getLayout(), flatCoordinates);
}
else {
geometry.appendCoordinate(coordinate);
}
The appendCoordinate method is a shortcut for the fairly common use case of adding a coordinate to the end of a LineString. To modify geometries with more control, set your desired coordinates with setCoordinates.
var maxCoords = 100;
var coords = lineString.getCoordinates(); // get coordinate array
coords.unshift(newCoord); // add to beginning of array
if (coords.length > maxCoords) {
coords.length = maxCoords;
}
lineString.setCoordinates(coords);

Three JS No Visual Update After Manually Editing Geometry

So I have a Geometry (the scope of this code is THREE.Geometry.prototype) and I am dynamically editing. newData is an object of { faces: [array of face Indexes], vertices: [array of vertice indexes]}. (these arrays maintain the length of the origin face and vertices arrays length and hold the form [null, null, null, "4", "5", null, null... ])
Using these arrays, I strip through all the faces and vertices and apply them to 1 of 2 new arrays, effectively splitting all the data into 2 groups. I also update the pointers on the faces!
In the end I know I've updated the geometry and it is correct, but the changes I make aren't getting displayed. I've tried .elementsNeedUpdate which causes and error. (no property 'a' of undefined in InitWebGlObjects... I looked there, couldn't see a reference to a)
I've tried vertices need update, it does nothing.
I've also tried updateCentroids in combination with the previous tool. It does nothing.
I've heard of not being able to resize the buffer. What is the buffer and the length of the buffer? The amount of verticies I'm giving to a model?
I've seen "You can emulate resizing by pre-allocating larger buffer and then keeping unneeded vertices collapsed / hidden." It sounds like that may be what I'm doing? How can I collapse/ hide a vertice? I haven't seen any references to that.
Thanks for your time!
var oldVertices = this.vertices
var oldFaces = this.faces;
var newVertices = []
var newFaces = [];
var verticeChanges = [];
this.vertices = [];
this.faces = [];
for(var i in oldVertices){
var curAr = ((newData.vertices[i]) ? (newVertices):(this.vertices));
curAr.push(oldVertices[i]);
verticeChanges[i] = curAr.length-1;
}
for(var i in oldFaces){
var curAr = ((newData.faces[i]) ? (newFaces):(this.faces));
oldFaces[i].a = verticeChanges[oldFaces[i].a];
oldFaces[i].b = verticeChanges[oldFaces[i].b];
oldFaces[i].c = verticeChanges[oldFaces[i].c];
}
console.log('Vertices Cut from', oldVertices.length, "to:", newVertices.length, 'and', this.vertices.length);
console.log('Faces Cut from', oldFaces.length, "to:", newFaces.length, 'and', this.faces.length);
I recently ran into this problem myself. I found that if I'm adding vertices and faces to the geometry, I need to set this.groupsNeedUpdate = true in order to tell the renderer to update it's internal buffers.
Maybe connected to this point from this tutorial:
"I just wanted to quickly point out a quick gotcha for Three.js, which
is that if you modify, for example, the vertices of a mesh, you will
notice in your render loop that nothing changes. Why? Well because
Three.js (as far as I can tell) caches the data for a mesh as
something of an optimisation. What you actually need to do is to flag
to Three.js that something has changed so it can recalculate whatever
it needs to. You do this with the following:
// set the geometry to dynamic so that it allow updates
sphere.geometry.dynamic = true;
// changes to the vertices
sphere.geometry.__dirtyVertices = true;
// changes to the normals
sphere.geometry.__dirtyNormals = true;"

Categories

Resources