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

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.

Related

Is javascript not fast enough for doing fluid simulation?

I am currently trying to implement a small fluid simulation on P5js. I tried to render 20K squares with a random colour. I got a frame rate of 2.xxx.
var sim;
var xdim = 200; var xLength;
var ydim = 100; var yLength;
function setup() {
createCanvas(800,400);
sim = new Sim(xdim, ydim);
}
function draw() {
xLength = width/xdim;
yLength = height/ydim;
for (var i = 0; i < xdim; ++i) for (var j = 0; j < ydim; ++j) {
fill(100);
rect(i*xLength, j*yLength, xLength, yLength);
}
console.log(frameRate());
}
What is the problem behind? Is the library not good enough? Or, the poor configuration of my computer? Or, javascript is not suitable for these kinds of implementation?
We can't help debug your code without an MCVE. Specifically, you haven't provided the Sim class, so we can't run your code at all.
But you need to take a step back and ask yourself this: what performance do you expect? You can't really complain about performance if you didn't have any expectations going in.
Also, you might want to figure out how many squares you can display before seeing a performance hit.
From there it's a game of looking for optimizations. You're going to have to do some profiling to understand exactly where your performance hit is. Maybe you display fewer squares, or maybe you lower the framerate, or maybe you do some pre-rendering. Again, what you do depends on exactly what your expectations and goals are.
I will say that you should take that call to console.log() out of your draw() loop. You should only use that for debugging, and it's not going to improve your performance to call that every single frame.

JavaScript nested loops make function execute too slowly

I have a function that includes some nested for loops and works as intended.
The issue however, is that it has to loop through a lot of data, meaning that it takes too long to execute and often freezes the browser while it executes.
Data got from server is in the order of 2000+ elements.
Can anyone suggest a more efficient way of writing this function that will reduce the number of iterations and speed up the process?
function markers(boxes) {
$.getJSON('empdata.json', function (data) {
var json = data;
for (var i = 0; i < boxes.length; i++) {
for(var v = 0; v < json.length; v++) {
//loop through parsed markers within bounds
var obj = json[v];
if (boxes[i].contains(new google.maps.LatLng(obj.latitude, obj.longitude))) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(obj.latitude, obj.longitude),
map: map,
});
} else {
console.log("out of box");
}
} //close bounds markers
} //close full loop parse
})
};
The first part of the function simply gets data about map locations from a static JSON file using the jQuery $.GetJSON function.
The second part loops through boundary boxes that have been created depending on the parameters given. Finally the third part loops through each item in the JSON file and creates a map pin if it is inside the given boundaries.
I think the problem is that there can many boxes to loop through and this multiplied by the 2000+ items in the JSON file means that function has to go through thousands of iterations each time.
The bottleneck in your code is not the nested loops itself, but rather it's the google marker print that is very very slow.
However you can do some things like:
avoid the REcreation of temporary variables like var obj = json[v]; because it creates a lot of overhead. It is better to reassign that variable. (This principle is also at the base of fast js gaming libraries like Phaser.io. Same situation for:
for (var i = 0 ...
for(var v = 0 ...
var marker = new google.maps.Marker({ ...
avoid recall of new google.maps.LatLng(obj.latitude, obj.longitude) the creation of new objects is more expensive than assignation.
you can replace the creation of markers with fusion tables
make markers as thinner as possible
Use DOM DocumentFragments (if you see here the print is almost instantaneous)
do not console log because it is REALLY slow! Better attach text to DOM div acting as on-page log.
go async! try delegate the print of a single marker to an async callback in order to avoid the time spent waiting the end of the marker creation.
what is the complexity of boxes[i].contains()? Look that it is in the inner loop, so it is run N * M times, where N is the length of boxes and M the length of json. In computation theory, the more is optimized this function, the faster the whole snipped runs.
EDIT:
Found an interesting quick script that benchmarks sync 2K marker generation vs async one. On my pc elapsed time dropped of about 22 times when going async.
The core function is:
var createmarker=function(coordinates,index) {
setTimeout(function() {
var neighborMarker = new google.maps.Marker({
position: coordinates,
map: map,
title:'Marker '+index,
icon: 'https://cloud.githubusercontent.com/assets/238439/4837489/46de6daa-5fd7-11e4-9622-0e1cc674f6b2.png'
});
markersArray.push(neighborMarker );
},10);
};
clearMarkers('Asynchronous',function(coordsArray,timeini) {
for(var index=0;index<coordsArray.length;index++) {
createmarker(coordsArray[index], index);
}
});
hi im not sure if this will be of any help but how im understanding it your function has to go trough a big data base and because of that its slow.
so would it be a good solution to divide your database so add a general parameter that can divide your gigantic database into 4-8 smaller ones and make sure or that it will go trough them one by one or just immediately to the one it has to go to
like i said dunno if its gonna be of any help

Improving rudimentary AI of Angular based chess game

I have created a chess game with Angular and chess.js and am trying to improve its rudimentary AI. The un-improved code currently lives at: https://gist.github.com/dexygen/8a19eba3c58fa6a9d0ff (or https://gist.githubusercontent.com/dexygen/8a19eba3c58fa6a9d0ff/raw/d8ee960cde7d30850c0f00f511619651396f5215/ng-chess)
What the AI currently consists of is checking whether the computer (black) has a move that checkmates (using chess.js' in_checkmate() method), and if so, mating the human (white), otherwise making a random move. To improve this I thought that instead of merely making a random move, I would have the AI check for white's counters to black's responses. Then, if White has checkmate, not including those black responses in the moves to randomly select from.
I would like to improve the AI within makeMove() (which currently merely delegates to makeRandomMove()) but I am finding this to be harder than expected. What I expected to be able to do was, not unlike mateNextMove() (refer to lines 155-168 of the gist), to check for in_checkmate() within a loop, except the loop will be nested to account for black responses and white counters to those responses.
Here is my first attempt at what I expected would work but it does not avoid checkmate when possible.
function makeMove(responses) {
var evaluator = new Chess();
var response;
var allowsMate;
var counters = [];
var candidates = [];
for (var i=0, n=responses.length; i<n; i++) {
response = responses[i];
allowsMate = false;
evaluator.load(chess.fen());
evaluator.move(response);
counters = evaluator.moves();
//console.log(evaluator.ascii());
//console.log(counters);
for (var j=0, k=counters.length; j<k; j++) {
evaluator.move(counters[j]);
if (evaluator.in_checkmate()) {
//console.log('in_checkmate');
allowsMate = true;
break;
}
}
if (!allowsMate) {
candidates.push(response);
}
}
return makeRandomMove(candidates);
}
In order to debug/test taking advantage of a little knowledge helps, specifically attempting an early "Scholar's Mate", see: http://en.wikipedia.org/wiki/Scholar%27s_mate. If Black's random moves make this impractical just start over, the opportunity presents itself as often as not. Qxf7# is the notation for the mating move of Scholars mate both in the wikipedia article and also as returned by chess.moves(). So I've tried to modify the inner for loop as follows:
for (var j=0, k=counters.length; j<k; j++) {
evaluator.move(counters[j]);
if (counters[j] == 'Qxf7#') {
console.log(evaluator.in_checkmate());
}
}
But I've had this return false and allow me to deliver the mate. What am I doing wrong (and who possibly wants to help me on this project)?
It seems to me from the code you posted that you are not undoing the moves you make. When you loop through all possible moves, you make that move, then check for a threat. You should then unmake the move. That is probably why your last test didn't work as well.

OpenLayers ModifyFeature not saving new vertices

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

Javascript+Canvas implementation of Game of Life not working

I'm having problems with my javascript+canvas implementation of Conways Game of Life.
The cells are being created just fine and the canvas boxed representing the cells are also being rendered fine. But somewhere along the way all cells seems to be set alive & aren't toggling.
For the life of me I can't understand what's wrong.
The javascript code is here and my implementation is here.
Can someone please tell me where I went wrong.
************************EDIT************************
I think I've figured out whats wrong. I'm passing an element of the 2d array to the reallyToggle() function, like so -
var reallyToggle = function(a) {
//code
}
reallyToggle(cell[i][j]);
I think the problem lies in this part of the code. Can anyone tell me how can I pass an element of an array to a function?
So your code is pretty obscure and overly-complicated to be honest. Creating a grid of custom Javascript function objects is way over-engineering: all you need is a 2D boolean array.
Problems like this are often easiest to solve if thought of as two separate problems: the problem space and the world space. The problem space is the area in which you solve the actual problem. The world space is the problem space mapped to a visual outcome. To separate it out for your problem, think of the problem space as the two dimensional array of booleans and then the world space is your canvas.
If you would like to clean up your simulation a bit, here is an approach that may help:
//Have some parameters that you can change around
var cellSize = 10;
var cellsWide = 100;
var cellsHigh = 100;
//Instantiate and initialize a 2d array to false
var theGrid = new Array();
for (var i=0; i<cellsWide; i++) {
theGrid.push(new Array());
for (var j=0; j<cellsHeight; j++) {
theGrid[i].push(false);
}
}
//Attach a click event to your canvas (assuming canvas has already been dropped on page
//at the assigned width/height
$('#simCanvas').click(function(e) {
var i = Math.floor((e.pageX - this.offsetLeft)/cellSize);
var j = Math.floor((e.pageY - this.offsetTop)/cellSize);
theGrid[i][j] = !theGrid[i][j];
});
This would be a much more succinct way for you to handle the problem space. The mapping of problem space to world space is a bit more straight-forward, but if you need help with that just let me know.
Cheers
This line is rather suspect :)
if (!run) run == true;
First of all, check MoarCodePlz answer. And about Your code:
Uncaught exception: ReferenceError: Undefined variable: gameLoop when pressing start.
All Your functions that are defined in initialize are not in global scope, so they are not reachable in global scope.
Please, use Firebug; Chrome Developer Tools, Opera Dragonfly or something to find out such errors. These tools really help while developing JS.

Categories

Resources